views:

6546

answers:

6

Is it possible to cancel a UIView animation while it is in progress? Or would I have to drop to the CA level?

i.e. I've done something like this (maybe setting an end animation action too):

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];

But before the animation completes and I get the animation ended event, I want to cancel it (cut it short). Is this possible? Googling around finds a few people asking the same question with no answers - and one or two people speculating that it can't be done.

A: 

I have the same problem; the APIs don't have anything to cancel some specific animation. The

+ (void)setAnimationsEnabled:(BOOL)enabled

disables ALL animations, and thus does not work for me. There's two solutions:

1) make your animated object a subview. Then, when you want to cancel the animations for that view, remove the view or hide it. Very simple, but you need to recreate the subview without animations if you need to keep it in view.

2) repeat the anim only one, and make a delegate selector to restart the anim if needed, like this:

-(void) startAnimation {
NSLog(@"startAnim alpha:%f", self.alpha);
[self setAlpha:1.0];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationRepeatCount:1];
[UIView setAnimationRepeatAutoreverses:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(pulseAnimationDidStop:finished:context:)];
[self setAlpha:0.1];
[UIView commitAnimations];
}

- (void)pulseAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
if(hasFocus) {
 [self startAnimation];
} else {
 self.alpha = 1.0;
}
}

-(void) setHasFocus:(BOOL)_hasFocus {
hasFocus = _hasFocus;
if(hasFocus) {
 [self startAnimation];
}
}

Problem with 2) is that there's always delay stopping the anim as it finishes the current animation cycle.

Hope this helps.

+19  A: 

The way I do it is to create a new animation to your end point. Set a very short duration and make sure you use the +setAnimationBeginsFromCurrentState: method to start from the current state. When you set it to YES, the current animation is cut short. Looks something like this:

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.1];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];
Stephen Darlington
Thanks Stephen. Actually if you read the comments under my question this is exactly what I did do in the end - but since you've given a good, clear, account of it here I'm marking it as accepted
Phil Nash
+21  A: 

Use:

[myView.layer removeAllAnimations];

Jim Heising
I found that I had to add __#import <QuartzCore/QuartzCore.h>__ to remove a compiler warning ;)
deanWombourne
Yessir, you are correct. That is required as well.
Jim Heising
So simple, and yet so hard to find. Thank you for this, I've just spent an hour trying to figure this out.
MikeyWard
A: 

To cancel an animation you simply need to set the property that is currently being animated, outside of the UIView animation. That will stop the animation wherever it is, and the UIView will jump to the setting you just defined.

tomtaylor
This is only partially true. It does stop the animation, but it does not stop it from firing delegate methods, most notably, the animationComplete handler, so if you have logic there, you will see problems.
Jasconius
A: 

I think this is broken since iOS4 as I was using he short animation solution and that doesn't seem to be functioning any more. Also, setting an animated property manually has no effect.

Bovine
A: 

Even if you cancel the animation in the ways above animation didStopSelector still runs. So if you have logic states in your application driven by animations you will have problems. For this reason with the ways described above I use the context variable of UIView animations. If you pass the current state of your program by the context param to the animation, when the animation stops your didStopSelector function may decide if it should do something or just return based on the current state and the state value passed as context.

Gorky