I'm running this animation on a user interaction, and sometimes the animation may be run again before the ongoing animation has finished. What I would like it to do is to cancel the previous animation and continue with the new one.
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.bounds = bounds;
}
completion:^(BOOL finished) {
if (finished) {
// Do some cleanup after animating.
}
}];
Visually, this seems to work, but in my completion block I am told that it finished in both cases, which causes the cleanup code to run prematurely. So the first animation's completion block runs immediately after the second one starts, with finished = YES
. I expected it to have a finished value of NO
and the second one (once it completes) to have YES
.
Is there a way to know if the animation completed or if it was cancelled by another?
Sidenote: I tried doing the same animation with CABasicAnimation
and then I get finished = NO
the first time and YES
the second time, so the behavior I'm getting seems to be specific to animateWithDuration
.
Here's a GIF showing the above code in action with a duration of 10 and the completion block updating the label. As you can see, finished
is YES
every time the animation is restarted with the animateWithDuration
call:
So I investigated this further and I think this is an iOS 8 specific thing, due to how animations are now additive by default, which means that the animations will continue and add up to each other rather than being cancelled.
In my searching I found a WWDC session called "Building Interruptible and Responsive Interactions" which talks about this. In the session they claim that the animations will all finish at the same time but this is not the case in my testing (see the GIF in the question). The workaround suggested in the session is to keep a counter going for how many times the animation started vs. completed and perform the post-animation actions when the counter reaches zero. This is a bit hacky, but works. Here's the question code adjusted for this:
// Add this property to your class.
self.runningAnimations++;
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.bounds = bounds;
}
completion:^(BOOL finished) {
if (--self.runningAnimations == 0) {
// Do some cleanup after animating.
}
}];
Obviously you'll need a separate counter for every type of animation so it can get a bit messy in the end, but I don't think there's a better way unless you start using CAAnimation
directly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With