ryan
ryan

Reputation: 1354

iOS - completion block in UIView animateWithDuration gets called too early

I'm trying to do some animation when a table view cell gets selected. For some reason, the completion block is getting called way too early. Even setting the duration to 10 seconds, the completion block gets called immediately.

[UIView animateWithDuration:10.0 animations:^{
    message.frame = newFrame;
} completion:^(BOOL finished) {
    NSLog(@"DONE???");
}];

Any thoughts on why this is happening? Thanks.

Upvotes: 35

Views: 22184

Answers (4)

jomafer
jomafer

Reputation: 2725

Posting this here in case it helps. In my case I was using a keyframe animation and the completion block was called immediately. Beware these concepts:

  • duration: The duration of the overall animation, measured in seconds. If you specify a negative value or 0, changes are made immediately and without animations.

  • frameStartTime: The time at which to start the specified animations. This value must be in the range 0 to 1, where 0 represents the start of the overall animation and 1 represents the end of the overall animation. For example, for an animation that is two seconds in duration, specifying a start time of 0.5 causes the animations to begin executing one second after the start of the overall animation.

  • frameDuration: The length of time over which to animate to the specified value. This value must be in the range 0 to 1 and indicates the amount of time relative to the overall animation length. If you specify a value of 0, any properties you set in the animations block update immediately at the specified start time. If you specify a nonzero value, the properties animate over that amount of time. For example, for an animation that is two seconds in duration, specifying a duration of 0.5 results in an animation duration of one second.

Setting any of these values wrong will interfere with the time the completion gets called.

Upvotes: 1

Luke
Luke

Reputation: 7220

It's also possible for the completion to be called early if the animation has no effect, e.g. setting the alpha of a view to the value it already has.

Upvotes: 12

John Erck
John Erck

Reputation: 9538

Yes. It is being called too early because it's being interrupted somehow. Probably by a modal presentation transition or perhaps something else. Depending on your needs, the following may be a solution you like. We avoid the conflict by manually delaying the execution of our animation code like so:

// To get this in Xcode very easily start typing, "dispatch_aft..."

// Note the "0.2". This ensures the outstanding animation gets completed before we start ours
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [UIView animateWithDuration:1.0 delay:0 options:0 animations:^{
        // Your animation code
    } completion:^(BOOL finished) {
        // Your completion code
    }];
});

Upvotes: 18

Stavash
Stavash

Reputation: 14304

From the UIView documentation:

completion

A block object to be executed when the animation sequence ends. This block has no return value and takes a single Boolean argument that indicates whether or not the animations actually finished before the completion handler was called. If the duration of the animation is 0, this block is performed at the beginning of the next run loop cycle. This parameter may be NULL.

What this means is that there isn't a guarantee that the code will be executed only when the animation is done. I'd advise you to check the "finished" parameter as a condition for execution.

Upvotes: 31

Related Questions