4
votes

I use the block base API for my animations on iOS.

One animation has a completion block and that block is called at the end of the animation, nice.

However, that animation can be fired multiple times when the user scrolls (the animation is on a UITableViewCell). When that happens the the completion block is called multiple times. The finished parameter of the block is always YES.

Since the animation is not actually finished (an other animation took place) I thought that the finished parameter would be NO, but it's not.

Did I miss something? How can I avoid the completion block to be called multiple times?

3
Are you doing this in Simulator for now or have you tried running this on an actual device? Running an animation on UITableViewCells while scrolling doesn't seem like the appropriate thing to do. What's your frame rate while scrolling?runmad

3 Answers

5
votes

The completion block is called multiple times simply because, in your case, your animation is fired multiple times. What is happening is that iOS invokes your animation block each time it is told so, probably in a separate thread. Then, for each animation it tracks its completion, and upon completion it calls the associated completion block. So basically, you see your completion block firing multiple times, one for each invocation of your animation. Note that the boolean value associated to a completion block is specific of that completion block, it does not refer in any way to a different animation.

To recap, what you are experiencing is simply the effect of concurrency. If this is not your intended behavior, then you need to modify your code accordingly. If you want your animations to fire one at a time, you may use NSLock (NSConditionLock for advanced control using an associate condition variable) or, if you prefer, a mutex and the Posix pthreads library directly, to create a critical section to be executed in a mutually exclusive fashion.

2
votes

Not sure when you're firing the animations and whether they loop (like a UIActivityView spinner or something) - sounds like it's every single pixel the table view is scrolling?

In any event, perhaps you could use the UIScrollView delegate methods and tell each cell to start animation on scrollViewWillBeginDragging: and tell each cell to stop at scrollViewDidEndDragging:

You could set a boolean isAnimating for your UITableViewCell and if an animation is currently underway, do nothing.

if (isAnimating) {
     // ... do nothing
} else {
     // Start your animation
}

Or stick with whatever you have now and use a boolean still, but only fire the animation if it's not currently animating. Then in your finished parameter just set isAnimating to NO.

if (isAnimating) {
         // ... do nothing
    } else {
         [UIView animateWithDuration:0.3f
                          animations:^{
                                     // animations...
                                     isAnimating = YES;
                                     }
                          completion:^{
                                     isAnimating = NO;
                                     }
          ];    
}
0
votes

I've resolved this issue by looking if the completion block is relevant at the beginning of that block.

The finished parameter is not relevant right now. I've communicated with Apple and they told me that it's fixed in iOS 4.2.