28

I'm setting up the following UIView animateWithDuration: method, with the intention of setting my animationOn BOOL elsewhere in the program to cancel that infinite looped repeat. I was under the impression that the completion block would be called each time a cycle of the animation ends, but this doesn't appear to be the case.

Is the completion block ever called in a repeating animation? And if not, is there another way I can stop this animation from outside this method?

- (void) animateFirst: (UIButton *) button
{
    button.transform = CGAffineTransformMakeScale(1.1, 1.1);
    [UIView animateWithDuration: 0.4
                          delay: 0.0
                        options: UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat
                     animations: ^{
                         button.transform = CGAffineTransformIdentity;
                     } completion: ^(BOOL finished){
                         if (!animationOn) {
                             [UIView setAnimationRepeatCount: 0];
                         }
    }];
}
tshepang
  • 12,111
  • 21
  • 91
  • 136
Luke
  • 9,512
  • 15
  • 82
  • 146

5 Answers5

56

The completion block will only get called when the animation is interrupted. For example it gets called when the app goes in the background and comes back to the foreground again (via multitasking). In that case the animation is stopped. You should restart the animation when that happens.

To stop the animation you can remove it from the view's layer:

[button.layer removeAllAnimations];
Tom van Zummeren
  • 9,130
  • 12
  • 52
  • 63
  • That didn't work, I'm afraid. Animation is still going after the change in transform is applied. (I set the `.transform` to `CGAffineTransformMakeScale(1.0, 1.0)`). – Luke Dec 21 '12 at 14:07
  • I found something else that might work. I updated my answer. Can you try that? – Tom van Zummeren Dec 21 '12 at 14:11
  • Oh you figured it out yourself :) Thanks for accepting my answer anyway – Tom van Zummeren Dec 21 '12 at 14:11
  • Yours appeared pretty much as I hit Add on mine, so I figured that meant you were typing it first ;) – Luke Dec 21 '12 at 14:12
  • 2
    @TomvanZummeren But this will remove every single animation.... what if you have multiple animation blocks and you only want to stop one of the animation blocks.... – Supertecnoboff Jul 08 '15 at 06:12
9

Old but another option.

You can also setup another animation which is not repeating on the same view, that way you can also capture it in the current state and return it to how it is by using the option UIViewAnimationOptionBeginFromCurrentState. Your completion block is also called.

-(void)someEventSoStop
{
    button.transform = CGAffineTransformMakeScale(1.0, 1.0);
    [UIView animateWithDuration: 0.4
                          delay: 0.0
                        options: UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState
                     animations: ^{
                         button.transform = CGAffineTransformIdentity;
                     } completion: ^(BOOL finished){

                     }];
}
Recycled Steel
  • 2,272
  • 3
  • 30
  • 35
  • This is a nicer approach than the other solution as it smoothly brings the animation back to identity. – Nikolozi Jan 11 '16 at 10:06
2

I've solved the problem by calling [button.layer removeAllAnimations].

Luke
  • 9,512
  • 15
  • 82
  • 146
1

As per the documentation of View class reference: If you used any of the class methods such as animateWithDuration:delay:options:animations:completion: if the duration is set to negative value or 0, the changes are made without performing animation. so I did something like this to stop the infinite animation:

[UIView animateWithDuration:0.0 animations:^{
      button.layer.affineTransform = CGAffineTransformIdentity;
  }];

I think this is better than removing all animations from the layer as in the suggested answer. Note that this is applicable for all other class animation methods in the UIView class.

Wael Showair
  • 3,092
  • 2
  • 22
  • 29
0

Calling removeAllAnimations() works. However, calling this method stops the animations and apply the transformations inside the UIView.animate block outright, thus causing an unsmooth transition. To be able to achieve a smooth transition when you remove animations, you can use UIView.transition which will smoothly transition the current view state to the final state using a cross-dissolve animation, hence achieving a smooth transition:

UIView.transition(with: stackView, duration: 0.3, options: .transitionCrossDissolve) {
    self.button.layer.removeAllAnimations()
    self.button.alpha = 1
}
Mohamed Salah
  • 868
  • 1
  • 15
  • 34