Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cancel previous animation when a new one is triggered?

Tags:

ios

swift

I am writing a camera app, and have trouble with showing the focus square when user tap on the screen.

My code is (in swift):

self.focusView.center = sender.locationInView(self.cameraWrapper)
self.focusView.transform = CGAffineTransformMakeScale(2, 2)
self.focusView.hidden = false

UIView.animateWithDuration(0.5, animations: { [unowned self] () -> Void in
    self.focusView.transform = CGAffineTransformIdentity
}, completion: { (finished) -> Void in
    UIView.animateWithDuration(0.5, delay: 1.0, options: nil, animations: { () -> Void in
        self.focusView.alpha = 0.0
    }, completion: { (finished) -> Void in
            self.focusView.hidden = true
            self.focusView.alpha = 1.0
    })
})

However, if use tap the screen consecutively when the previous animation does not finish, the old and new animation will mix up and the focus view will behave strangely, for example it will disappear very quick.

Could anyone tell me how to cancel previous animation, especially the previous completion block?

like image 404
HanXu Avatar asked Feb 04 '15 09:02

HanXu


3 Answers

You can user method removeAllAnimations to stop animation
Replace your code with below

self.focusView.center = sender.locationInView(self.cameraWrapper)
self.focusView.transform = CGAffineTransformMakeScale(2, 2)
self.focusView.hidden = false
self.focusView.layer.removeAllAnimations() // <<====  Solution
UIView.animateWithDuration(0.5, animations: { [unowned self] () -> Void in
    self.focusView.transform = CGAffineTransformIdentity
}, completion: { (finished) -> Void in

    UIView.animateWithDuration(0.5, delay: 1.0, options: nil, animations: { () -> Void in
        self.focusView.alpha = 0.0
    }, completion: { (finished) -> Void in
            self.focusView.hidden = true
            self.focusView.alpha = 1.0
    })
})


Reference : link
enter image description here

like image 51
Jageen Avatar answered Nov 20 '22 15:11

Jageen


@Jageen solution is great, but I worked with UIStackView animation and there I needed additional steps. I have stackView with view1 and view2 inside, and one view should be visible and one hidden:

public func configureStackView(hideView1: Bool, hideView2: Bool) {
    let oldHideView1 = view1.isHidden
    let oldHideView2 = view2.isHidden
    view1.layer.removeAllAnimations()
    view2.layer.removeAllAnimations()
    view.layer.removeAllAnimations()
    stackView.layer.removeAllAnimations()
    // after stopping animation the values are unpredictable, so set values to old
    view1.isHidden = oldHideView1 //    <- Solution is here
    view2.isHidden = oldHideView2 //    <- Solution is here

    UIView.animate(withDuration: 0.3,
                   delay: 0.0,
                   usingSpringWithDamping: 0.9,
                   initialSpringVelocity: 1,
                   options: [],
                   animations: {
                    view1.isHidden = hideView1
                    view2.isHidden = hideView2
                    stackView.layoutIfNeeded()
    },
                   completion: nil)
}
like image 28
Paul T. Avatar answered Nov 20 '22 14:11

Paul T.


In my case, I add the focusing indicator by using addSubview().

self.view.addSubview(square)

square is the UIView I defined in the function. So when the user tap the screen to focus, this function will add a square on subview. And to cancel this animation when the next tap happen, I just simply use the removeFromSuperview() function, when this function called it removes the view from its superview which is the focusing square here.

filterView.subviews.forEach({ $0.removeFromSuperview() })

It's different from the method above to remove the animation, but remove the subview directly.

like image 1
Ron Avatar answered Nov 20 '22 14:11

Ron