I have a Pan and Tap gestures, They don't seem to get recognized when I try to activate them on the simulator.
I don't get any error, tried to debugging, it seems that it does assign the gestures, but won't call the methods I try to use when they happened, like it just don't recognize them at all.
This is what I have:
dismissIndicator: (Attaching the Pan recognizer to this view):
let dismissIndicator: UIView = {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.appWhite
view.layer.cornerRadius = 15
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
let dismissImg = UIImageView(image: UIImage(named: "closeArrow"))
dismissImg.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(dismissImg)
dismissImg.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
dismissImg.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
return view
}()
This is how I set the gestures:
let dimView = UIView()
let detailView = DetailsView()
var currentPost: Post? = nil
init(post: Post) {
super.init()
dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))
let panGesture = PanDirectionGestureRecognizer(direction: .vertical(.down), target: self, action: #selector(panGestureRecognizerHandler(_:)))
detailView.addGestureRecognizer(panGesture)
setPostDetails(post: post)
}
This is handleDismiss (Which does not get called when tapping on the DimView:
#objc func handleDismiss(){
UIView.animate(withDuration: 0.5) {
self.dimView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.detailView.frame = CGRect(x: 0, y: window.frame.height, width: self.detailView.frame.width, height: self.detailView.frame.height)
}
}
}
This is my panGestureRecognizerHandler:
#objc func panGestureRecognizerHandler(_ sender: UIPanGestureRecognizer) {
let touchPoint = sender.location(in: detailView.dismissIndicator.window)
var initialTouchPoint = CGPoint.zero
switch sender.state {
case .began:
initialTouchPoint = touchPoint
case .changed:
if touchPoint.y > initialTouchPoint.y {
detailView.frame.origin.y = initialTouchPoint.y + touchPoint.y
}
case .ended, .cancelled:
if touchPoint.y - initialTouchPoint.y > 300 {
handleDismiss()
} else {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
if let window = UIApplication.shared.keyWindow {
let height = window.frame.height - 80
let y = window.frame.height - height
self.dimView.alpha = 1
self.detailView.frame = CGRect(x: 0, y: y, width: self.detailView.frame.width, height: self.detailView.frame.height)
}
})
}
case .failed, .possible:
break
default:
break
}
}
This is where I add dimView and detailView to the screen:
func showDetailView(){
if let window = UIApplication.shared.keyWindow {
dimView.backgroundColor = UIColor(white: 0, alpha: 0.5)
window.addSubview(dimView)
window.addSubview(detailView)
let height = window.frame.height - 80
let y = window.frame.height - height // This is so we can later slide detailView all the way down.
detailView.frame = CGRect(x: 0, y: window.frame.height, width: window.frame.width, height: height)
dimView.frame = window.frame
dimView.alpha = 0
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.dimView.alpha = 1
self.detailView.frame = CGRect(x: 0, y: y, width: self.detailView.frame.width, height: self.detailView.frame.height)
})
}
}
Related
We have a broken transition in WeatherKit only reproducible on iOS 13 beta. We're unsure if this is an UIKit bug or we're doing something awfully wrong.
With an array of UIViewPropertyAnimator working before iOS 13, ever since iOS 13 (through all of the betas) the animation frame is not updating correctly. For example, I have an UIViewPropertyAnimator called labelAnimator which animates a label to some specific CGRect, that CGRect is not respected and the label animates somewhere else as shown in the video.
Curious enough, if I mess around with the order of the transitions in the array, the bottom sheet works fine and the only one that animates wrong is the temperature label.
Here's the code that animates that whole view:
class MainView: UIViewController {
var panGesture = UIPanGestureRecognizer()
var tapGesture = UITapGestureRecognizer()
let animationDuration: TimeInterval = 0.75
var diff: CGFloat = 150
#IBOutlet weak var gradientView: GradientView!
#IBOutlet weak var detailedViewContainer: UIView!
#IBOutlet weak var blurView: UIVisualEffectView!
override func viewDidLoad() {
self.panGesture.addTarget(self, action: #selector(MainView.handlePanGesture(gesture:)))
self.detailedViewContainer.addGestureRecognizer(self.panGesture)
self.tapGesture.addTarget(self, action: #selector(MainView.handleTapGesture(gesture:)))
self.detailedViewContainer.addGestureRecognizer(self.tapGesture)
}
enum PanelState {
case expanded
case collapsed
}
var nextState: PanelState {
return panelIsVisible ? .collapsed : .expanded
}
var panelIsVisible: Bool = false
var runningAnimations = [UIViewPropertyAnimator]()
var animationProgressWhenInterrupted: CGFloat = 0.0
#objc func handleTapGesture(gesture: UITapGestureRecognizer) {
switch gesture.state {
case .ended:
tapAnimation()
default: break
}
}
#objc func tapAnimation(){
self.panGesture.isEnabled = false
self.tapGesture.isEnabled = false
startInteractiveTransition(state: nextState, duration: animationDuration)
updateInteractiveTransition(fractionComplete: 0)
let linearTiming = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.8, y: -0.16), controlPoint2: CGPoint(x: 0.22, y: 1.18))
continueInteractiveTransition(timingParameters: linearTiming){
self.panGesture.isEnabled = true
self.tapGesture.isEnabled = true
}
}
#objc func handlePanGesture(gesture: UIPanGestureRecognizer) {
switch gesture.state {
case .began:
if !panelIsVisible ? gesture.velocity(in: nil).y < 0 : gesture.velocity(in: nil).y > 0 {
startInteractiveTransition(state: nextState, duration: animationDuration)
}
case .changed:
let translation = gesture.translation(in: self.detailedViewContainer)
var fractionComplete = (translation.y / view.bounds.height * 2)
fractionComplete = !panelIsVisible ? -fractionComplete : fractionComplete
updateInteractiveTransition(fractionComplete: fractionComplete)
case .ended:
let linearTiming = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.8, y: -0.16), controlPoint2: CGPoint(x: 0.22, y: 1.18))
continueInteractiveTransition(timingParameters: linearTiming) {
self.panGesture.isEnabled = true
self.tapGesture.isEnabled = true
}
NotificationCenter.default.post(name: .resetHeaders, object: nil)
NotificationCenter.default.post(name: .disableScrolling, object: nil, userInfo: ["isDisabled": nextState == .collapsed])
default:
break
}
}
// MARK: - Animations
func animateTransitionIfNeeded(state: PanelState, duration: TimeInterval) {
if runningAnimations.isEmpty {
// MARK: Frame
var linearTiming = UICubicTimingParameters(animationCurve: .easeOut)
linearTiming = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.1, y: 0.1), controlPoint2: CGPoint(x: 0.1, y: 0.1))
let frameAnimator = UIViewPropertyAnimator(duration: duration, timingParameters: linearTiming)
frameAnimator.addAnimations {
switch state {
case .expanded:
self.detailedViewContainer.frame = CGRect(x: 0, y: self.diff, width: self.view.bounds.width, height: self.view.bounds.height - self.diff)
case .collapsed:
self.detailedViewContainer.frame = CGRect(x: 0, y: self.view.bounds.height - self.view.safeAreaInsets.bottom - 165, width: self.view.bounds.width, height: 200)
}
}
// MARK: Arrow
let arrowAnimator = UIViewPropertyAnimator(duration: duration, timingParameters: linearTiming)
arrowAnimator.addAnimations {
switch state {
case .expanded:
self.leftArrowPath.transform = CGAffineTransform(rotationAngle: 15 * CGFloat.pi / 180)
self.rightArrowPath.transform = CGAffineTransform(rotationAngle: 15 * -CGFloat.pi / 180)
case .collapsed:
self.leftArrowPath.transform = CGAffineTransform(rotationAngle: 15 * -CGFloat.pi / 180)
self.rightArrowPath.transform = CGAffineTransform(rotationAngle: 15 * CGFloat.pi / 180)
}
self.leftArrowPath.center.y = self.detailedViewContainer.frame.origin.y + 15
self.rightArrowPath.center.y = self.detailedViewContainer.frame.origin.y + 15
}
// MARK: Scale
let radiusAnimator = UIViewPropertyAnimator(duration: duration, timingParameters: linearTiming)
radiusAnimator.addAnimations{
switch state {
case .expanded:
self.gradientView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
self.gradientView.layer.maskedCorners = [.layerMaxXMinYCorner,.layerMinXMinYCorner]
self.gradientView.layer.cornerRadius = dataS.hasTopNotch ? 20 : 14
case .collapsed:
self.gradientView.transform = CGAffineTransform.identity
self.gradientView.layer.maskedCorners = [.layerMaxXMinYCorner,.layerMinXMinYCorner]
self.gradientView.layer.cornerRadius = 0
}
}
// MARK: Blur
let blurTiming = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.5, y: 0.25), controlPoint2: CGPoint(x: 0.5, y: 0.75))
let blurAnimator = UIViewPropertyAnimator(duration: duration, timingParameters: blurTiming)
blurAnimator.addAnimations {
switch state {
case .expanded:
self.blurView.effect = UIBlurEffect(style: .dark)
case .collapsed:
self.blurView.effect = nil
}
}
// MARK: Text
let textAnimator = UIViewPropertyAnimator(duration: duration, timingParameters: linearTiming)
textAnimator.addAnimations({
switch state{
case .expanded:
self.tempLabel.transform = CGAffineTransform(scaleX: 0.6, y: 0.6)
self.tempLabel.frame = CGRect(origin: CGPoint(x: 15, y: self.diff / 2 - self.tempLabel.frame.height / 2), size: self.tempLabel.frame.size)
self.descriptionLabel.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
self.descriptionLabel.alpha = 0
self.descriptionLabel.transform = CGAffineTransform(translationX: 0, y: -100)
self.summaryLabel.frame = CGRect(origin: CGPoint(x: self.blurView.contentView.center.x, y: 10), size: self.summaryLabel.frame.size)
case .collapsed:
self.descriptionLabel.transform = CGAffineTransform.identity
self.descriptionLabel.alpha = 1
self.tempLabel.transform = CGAffineTransform.identity
self.tempLabel.frame = CGRect(origin: CGPoint(x: 15, y: self.view.frame.height / 2 - self.tempLabel.frame.height / 2 - 30), size: self.tempLabel.frame.size)
self.summaryLabel.frame = CGRect(origin: CGPoint(x: self.blurView.contentView.center.x, y: self.tempLabel.center.y - self.summaryLabel.frame.height / 2), size: self.summaryLabel.frame.size)
}
}, delayFactor: 0.0)
let summaryLabelTiming = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.05, y: 0.95), controlPoint2: CGPoint(x: 0.15, y: 0.95))
let summaryLabelTimingReverse = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.95, y: 0.5), controlPoint2: CGPoint(x: 0.85, y: 0.05))
// MARK: Summary Label
let summaryLabelAnimator = UIViewPropertyAnimator(duration: duration, timingParameters: state == .collapsed ? summaryLabelTiming : summaryLabelTimingReverse)
summaryLabelAnimator.addAnimations {
switch state{
case .expanded:
self.summaryLabel.alpha = 1
case .collapsed:
self.summaryLabel.alpha = 0
}
}
radiusAnimator.startAnimation()
runningAnimations.append(radiusAnimator)
blurAnimator.scrubsLinearly = false
blurAnimator.startAnimation()
runningAnimations.append(blurAnimator)
summaryLabelAnimator.scrubsLinearly = false
summaryLabelAnimator.startAnimation()
runningAnimations.append(summaryLabelAnimator)
frameAnimator.startAnimation()
runningAnimations.append(frameAnimator)
textAnimator.startAnimation()
textAnimator.pauseAnimation()
runningAnimations.append(textAnimator)
arrowAnimator.startAnimation()
runningAnimations.append(arrowAnimator)
// Clear animations when completed
runningAnimations.last?.addCompletion { _ in
self.runningAnimations.removeAll()
self.panelIsVisible = !self.panelIsVisible
textAnimator.startAnimation()
}
}
}
/// Called on pan .began
func startInteractiveTransition(state: PanelState, duration: TimeInterval) {
if runningAnimations.isEmpty {
animateTransitionIfNeeded(state: state, duration: duration)
for animator in runningAnimations {
animator.pauseAnimation()
animationProgressWhenInterrupted = animator.fractionComplete
}
}
let hapticSelection = SelectionFeedbackGenerator()
hapticSelection.prepare()
hapticSelection.selectionChanged()
}
/// Called on pan .changed
func updateInteractiveTransition(fractionComplete: CGFloat) {
for animator in runningAnimations {
animator.fractionComplete = fractionComplete + animationProgressWhenInterrupted
}
}
/// Called on pan .ended
func continueInteractiveTransition(timingParameters: UICubicTimingParameters? = nil, durationFactor: CGFloat = 0, completion: #escaping ()->()) {
for animator in runningAnimations {
animator.continueAnimation(withTimingParameters: timingParameters, durationFactor: durationFactor)
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + animationDuration) {
completion()
}
}
}
And here's a video of the issue in iOS 13 and how it currently works in iOS 12.
I have same issue, for me UIViewPropertyAnimator continueAnimation durationFactor parameter is the issue, whenever it is not 0, after few animation the table view goes crazy.
When user open my app for the first time, my custom view doesn't appear. But the next time it works fine.
I am calling this view when user taps on button.
let menu = MenuView(image: image, title: "text", buttons: buttons)
menu.show(animated: true)
Custom View code
class MenuView: UIView, Menu {
var background = UIView()
var blackOverlay = UIView()
convenience init(image: UIImage, title: String, buttons: [UIButton]) {
self.init(frame: UIScreen.main.bounds)
setupView(image: image, title: title, buttons: buttons)
}
#objc func cancelTapped() {
hide(animated: true)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView(image: UIImage, title: String, buttons: [UIButton]) {
blackOverlay.backgroundColor = UIColor.black.withAlphaComponent(0.5)
blackOverlay.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
addSubview(blackOverlay)
let backgroundWidth = self.frame.width - CGFloat(80.0)
let imageView = UIImageView(frame: CGRect(x: (backgroundWidth/2)-17, y: 40, width: 34, height: 34))
imageView.image = image
imageView.contentMode = .center
background.addSubview(imageView)
let titleLabel = UILabel()
let stringHeight = title.stringHeight + 14
titleLabel.frame = CGRect(x: background.center.x+8, y: 100, width: backgroundWidth-16, height: stringHeight)
titleLabel.font = UIFont.systemFont(ofSize: 15, weight: .regular)
titleLabel.text = title
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 0
background.addSubview(titleLabel)
var newHeight: CGFloat = 0
for i in 0...buttons.count-1 {
buttons[i].frame.origin = CGPoint(x: 0, y: titleLabel.frame.height + 125 + CGFloat(i*50))
buttons[i].frame.size = CGSize(width: backgroundWidth, height: 50)
buttons[i].setTitleColor(.gingerColor, for: .normal)
buttons[i].setTitleColor(UIColor.gingerColor.withAlphaComponent(0.5), for: .highlighted)
buttons[i].titleLabel?.textAlignment = .center
newHeight+=buttons[i].frame.height
let separator = UIView()
separator.frame.origin = CGPoint(x: 0, y: titleLabel.frame.height + 125 + CGFloat(i*50))
separator.frame.size = CGSize(width: frame.width, height: 1)
separator.backgroundColor = UIColor(hexString: "dedede")
background.addSubview(separator)
if i == buttons.count-1 {
buttons[i].setTitleColor(UIColor(hexString: "9E9E9E"), for: .normal)
}
buttons[i].addTarget(self, action: #selector(self.cancelTapped), for: .touchUpInside)
background.addSubview(buttons[i])
}
background.frame.origin = CGPoint(x: center.x, y: frame.height)
background.frame.size = CGSize(width: frame.width-80, height: 90 + titleLabel.frame.height+imageView.frame.height + CGFloat(newHeight))
background.backgroundColor = .white
background.layer.cornerRadius = 16
background.layer.masksToBounds = true
addSubview(background)
}
}
and my custom view protocol & extension code:
protocol Menu {
func show(animated: Bool)
func hide(animated: Bool)
var blackOverlay: UIView { get }
var background: UIView { get }
}
extension Menu where Self: UIView {
func show(animated: Bool) {
self.blackOverlay.alpha = 0
self.background.alpha = 0
self.blackOverlay.center = self.center
self.background.center = CGPoint(x: self.center.x, y: self.frame.height+self.background.frame.height/2)
UIApplication.shared.delegate?.window??.rootViewController?.view.addSubview(self)
if animated {
UIView.animate(withDuration: 0.33, animations: {
self.blackOverlay.alpha = 1
self.background.alpha = 1
})
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 10, options: UIViewAnimationOptions(rawValue: 0), animations: {
self.background.center = self.center
}, completion: { (completed) in
print("completed is \(completed)")
})
} else {
self.blackOverlay.alpha = 1
self.background.alpha = 1
self.background.center = self.center
}
}
func hide(animated: Bool) {
if animated {
UIView.animate(withDuration: 0.33, animations: {
self.blackOverlay.alpha = 0
self.background.alpha = 0
})
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 10, options: UIViewAnimationOptions(rawValue: 0), animations: {
self.background.center = CGPoint(x: self.center.x, y: self.frame.height + self.background.frame.height/2)
}, completion: { (completed) in
self.removeFromSuperview()
})
} else {
self.removeFromSuperview()
}
}
}
As you can see I have a parameter completed in completion animation block, it returns false for the first time. All subsequent times it returns false
I found my mistake.
I tried to present my custom view controller above rootController. But, for the very first time user's rootcontroller is the first screen of onboarding. So, my custom view tried to be shown on another controller.
I am facing issue in frame while doing rotation animation in custom UIBarButtonItem.
My code is as follow:
class ViewController: UIViewController {
var bellIcon: UIBarButtonItem = UIBarButtonItem()
override func viewDidLoad() {
super.viewDidLoad()
setupBellButton()
}
func setupBellButton(){
let icon = UIImage(named: "Bell.png")
let iconSize = CGRect(origin: CGPoint.zero, size: icon!.size)
let iconButton = UIButton(frame: iconSize)
iconButton.setBackgroundImage(icon, for: .normal)
let badgeView = UIView(frame: CGRect(origin: CGPoint(x: iconButton.frame.maxX-10, y: iconButton.frame.minX), size: CGSize(width: 10, height: 10)))
badgeView.layer.cornerRadius = 5
badgeView.layer.borderColor = UIColor.red.cgColor
badgeView.backgroundColor = UIColor.red
badgeView.layer.masksToBounds = true
badgeView.clipsToBounds = true
let btnContainer = UIView(frame: CGRect(origin: CGPoint.zero, size: icon!.size))
btnContainer.addSubview(iconButton)
btnContainer.addSubview(badgeView)
badgeView.transform = CGAffineTransform(scaleX: 0, y: 0)
UIView.animate(withDuration: 1) {
badgeView.transform = CGAffineTransform(scaleX: 1, y: 1)
}
bellIcon.customView = btnContainer
iconButton.addTarget(self, action:#selector(bellIconTapped), for: .touchUpInside)
navigationItem.rightBarButtonItem = bellIcon
bellIcon.customView?.backgroundColor = UIColor.purple
}
#IBAction func bellIconTapped(_ sender: Any) {
UIView.animate(withDuration: 0.3, animations: {
self.bellIcon.customView!.transform = CGAffineTransform(rotationAngle: -0.4)
}) { (true) in
UIView.animate(withDuration: 0.3, animations: {
self.bellIcon.customView!.transform = CGAffineTransform(rotationAngle: 0.4)
}, completion: { (true) in
UIView.animate(withDuration: 0.4, animations: {
self.bellIcon.customView!.transform = CGAffineTransform.identity
})
})
}
}
}
Thanks in advance!!!
I have resolved the above issue for both iOS 10 and 11 by simply adding the constant to the 'bellIcon.customView'
I have added following code at the end of the 'setupBellButton()'
bellIcon.customView?.translatesAutoresizingMaskIntoConstraints = false
let widthConstraint = bellIcon.customView?.widthAnchor.constraint(equalToConstant: 25.0)
let heightConstraint = bellIcon.customView?.heightAnchor.constraint(equalToConstant: 25.0)
bellIcon.customView?.addConstraints([widthConstraint!, heightConstraint!])
Happy Coding!!!
Everything works perfectly except that i cant zoom in on the imageview.
var zoomingImageView = UIImageView()
var startingFrame : CGRect?
var blackBackground : UIScrollView?
var startingImageView: UIImageView?
this is where i set up the scrollview and the imageview inside of it
func performZoomingForStartImageView(imageView: UIImageView){
if let keyWindow = UIApplication.shared.keyWindow {
blackBackground = UIScrollView(frame: CGRect(x: self.view.frame.origin.x, y: self.view.frame.origin.y, width: self.view.frame.size.width, height: self.view.frame.size.height))
blackBackground?.delegate = self
blackBackground?.maximumZoomScale = 4.0
blackBackground?.minimumZoomScale = 1.0
blackBackground?.isScrollEnabled = true
blackBackground?.backgroundColor = .black
blackBackground?.alpha = 0.0
blackBackground?.isUserInteractionEnabled = true
blackBackground?.contentSize = CGSize(width: 0, height: self.view.frame.size.height)
keyWindow.addSubview(blackBackground!)
self.startingImageView = imageView
self.startingImageView?.isHidden = true
startingFrame = imageView.superview?.convert(imageView.frame, to: nil)
zoomingImageView = UIImageView(frame: startingFrame!)
zoomingImageView.image = startingImageView?.image
zoomingImageView.contentMode = .scaleAspectFit
zoomingImageView.isUserInteractionEnabled = true
zoomingImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleZoomOut)))
zoomingImageView.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(self.saveImage)))
blackBackground?.addSubview(zoomingImageView)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut,.allowUserInteraction], animations: {
self.blackBackground?.alpha = 1.0
self.inputContainerView.alpha = 0
//let height = startingFrame!.height / startingFrame!.height * keyWindow.frame.widthimagedShown
self.zoomingImageView.frame = CGRect(x: self.view.frame.origin.x, y: self.view.frame.origin.y, width: self.view.frame.size.width, height: self.view.frame.size.height)
self.zoomingImageView.center = keyWindow.center
}, completion: nil)
}
}
this is supposed to allow the zooming but the print never gets called and i have set up the delegate
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
print("hi")
return zoomingImageView
}
For Swift 3 the method signature has changed.
Swift 3:
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}
I have a very simple sample that allows to drag an UIView around. When touch up, I will have an inertia effect on the dragging direction for a second. While if I touch down again, I need to stop all the inertia animation and start to do another drag. Here is my code, the "clearAllAnimations" doesn't stop my animation. How could I implement that?
import UIKit
class ViewController: UIViewController {
var tile : UIView = UIView()
var labelView = UITextView()
var displayLink : CADisplayLink?
override func viewDidLoad() {
super.viewDidLoad()
tile.frame = CGRect(x: 0, y: 0, width: 256, height: 256)
tile.backgroundColor = UIColor.redColor()
view.addSubview(tile)
var panGesture = UIPanGestureRecognizer(target: self, action: Selector("panHandler:"))
view.addGestureRecognizer(panGesture)
labelView.frame = CGRect(x: 0, y: 100, width: view.frame.width, height: 44)
labelView.backgroundColor = UIColor.clearColor()
view.addSubview(labelView)
}
func panHandler (p: UIPanGestureRecognizer!) {
var translation = p.translationInView(view)
if (p.state == UIGestureRecognizerState.Began) {
self.tile.layer.removeAllAnimations()
}
else if (p.state == UIGestureRecognizerState.Changed) {
var offsetX = translation.x
var offsetY = translation.y
var newLeft = tile.frame.minX + offsetX
var newTop = tile.frame.minY + offsetY
self.tile.frame = CGRect(x: newLeft, y: newTop, width: self.tile.frame.width, height: self.tile.frame.height)
labelView.text = "x: \(newLeft); y: \(newTop)"
p.setTranslation(CGPoint.zeroPoint, inView: view)
}
else if (p.state == UIGestureRecognizerState.Ended) {
var inertia = p.velocityInView(view)
var offsetX = inertia.x * 0.2
var offsetY = inertia.y * 0.2
var newLeft = tile.frame.minX + offsetX
var newTop = tile.frame.minY + offsetY
UIView.animateWithDuration(1, delay: 0, options:UIViewAnimationOptions.CurveEaseOut, animations: {_ in
self.tile.frame = CGRect(x: newLeft, y: newTop, width: self.tile.frame.width, height: self.tile.frame.height)
}, completion: nil)
}
}
}
Setting UIViewAnimationOptions.AllowUserInteraction does the trick. The new code to start animation is this:
UIView.animateWithDuration(animationDuration, delay: 0, options:UIViewAnimationOptions.CurveEaseOut | UIViewAnimationOptions.AllowUserInteraction | UIViewAnimationOptions.BeginFromCurrentState, animations: {_ in
self.tile.frame = CGRect(x: newLeft, y: newTop, width: self.tile.frame.width, height: self.tile.frame.height)
}, completion: nil)