Search code examples
iphonecore-animation

setAnimationRepeatAutoreverses not behaved as I expected


I am starting to learn to use UIView animation. So I wrote the following lines:

[UIView beginAnimations:nil context:NULL];

[UIView setAnimationDuration:2.0];
[UIView setAnimationRepeatCount:2];
[UIView setAnimationRepeatAutoreverses:YES];

CGPoint position = greenView.center;
position.y = position.y + 100.0f;
position.x = position.x + 100.0f;
greenView.center = position;

[UIView commitAnimations];

In this case, the UIView (a green box) moved back and fore 2 times. So far so good, BUT I found out that after the moving twice, the green box ended up jumped to the "new position" (position.x + 100.0f, position.y + 100.0f) instead of going back to the original position (position.x, position.y). That makes the animation look pretty odd (like after the box swing back to the original position caused by setAnimationRepeatAutoreverses, it jumps back to the new position in the last microsecond!)

What is the best way to make the green box NOT jump to the new position at the very last minute?


Solution

  • I've had exactly the same issue with using UIView animations on the alpha property. The worst part is that the animation jumps to the "final" position before the animationDidStop: delegate message is sent, which means you can't manually set the original state there.

    My solution was to use the animationDidStop delegate messages to create a new animation block, and string them all together:

    - (void)performAnimation
    {
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDelegate:self];
        [UIView setAnimationDidStopSelector:@selector(phase1AnimationDidStop:finished:context:)];
    
        // Do phase 1 of your animations here
        CGPoint center = someView.center;
        center.x += 100.0;
        center.y += 100.0;
        someView.center = center;
    
        [UIView commitAnimations];
    }
    
    - (void)phase1AnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
    {
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDelegate:self];
        [UIView setAnimationDidStopSelector:@selector(phase2AnimationDidStop:finished:context:)];
    
        // Do phase 2 of your animations here
        CGPoint center = someView.center;
        center.x -= 100.0;
        center.y -= 100.0;
        someView.center = center;
    
        [UIView commitAnimations];
    }
    
    - (void)phase2AnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
    {
        // Perform cleanup (if necessary)
    }
    

    You can string together as many animation blocks as you want this way. It seems wasteful code-wise, but until Apple give us a -[UIView setAnimationFinishesInOriginalState:] property or something similar, this the only way that I've found to solve this problem.