31

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?

HanXu
  • 5,507
  • 6
  • 52
  • 76

5 Answers5

47

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

Jageen
  • 6,345
  • 2
  • 37
  • 56
  • I tried your method and it does seem to remove the current animation but the new animation doesn't run correctly. My button calls an animation. When I tap the button, the animation starts. When I tap it again, I want the animation to stop and restart from the beginning. But, using your remove method, it stops and then runs some very short and messed up animation. Any idea as to why? – Dave G Sep 08 '15 at 03:16
  • Just posted it a few hours ago: http://stackoverflow.com/questions/32449016/consecutive-animation-calls-not-working/32449640#32449640 – Dave G Sep 08 '15 at 06:13
  • 8
    make sure you check `finished` in the completion block. it will be false if you did `removeAllAnimations()` for any animations that were in progress, in which case you probably want to skip the steps in that completion block. – Elijah Apr 11 '17 at 23:13
2

@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)
}
Paul T.
  • 4,938
  • 7
  • 45
  • 93
1

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.

Ron
  • 110
  • 1
  • 9
0

In my case, i just needed to set the value, i animated to concrete value.

For e.g.

 if ([indexPath isEqual:self.currentPlayerOrderIndexPath])
    {
        [UIView animateWithDuration:0.5
                              delay:0.0
                            options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveEaseInOut
                         animations:^{
                             cell.avatarImageV.transform = CGAffineTransformMakeScale(1.15,1.15);
                         }
                         completion:NULL];
    }
    else
    {
        cell.avatarImageV.transform = CGAffineTransformMakeScale(1.0, 1.0);
Nike Kov
  • 12,630
  • 8
  • 75
  • 122
0
extension CALayer {
    func removeAllAnimationsRecursive() {
        removeAllAnimations()
        sublayers?.forEach {
            $0.removeAllAnimationsRecursive()
        }
    }
}
Fedy_
  • 13
  • 1
  • 3
  • Remember that Stack Overflow isn't just intended to solve the immediate problem, but also to help future readers find solutions to similar problems, which requires understanding the underlying code. This is especially important for members of our community who are beginners, and not familiar with the syntax. Given that, **can you [edit] your answer to include an explanation of what you're doing** and why you believe it is the best approach? – Jeremy Caney Jun 21 '23 at 00:40