setAnimationRepeatAutoreverses not behaved as I ex

2019-05-02 08:48发布

问题:

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?

回答1:

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.



回答2:

I find out using an even number for setAnimationDuration will end repeat animation to its initial position and then pop to the final position after repeat animation ends. If I set setAnimationDuration to half cycle then the repeat animation will end to the final position. This is a cleaner way to handle animation than using setAnimationDidStopSelector: method.

// Repeat Swimming
CGPoint position = swimmingFish.center;
position.y = position.y - 100.0f;
position.x = position.x - 100.0f;

[UIView beginAnimations:nil context:nil]; 
[UIView setAnimationDuration:1.0f];
[UIView setAnimationRepeatCount:2.5];
[UIView setAnimationRepeatAutoreverses:YES];

swimmingFish.center = position;