Multiple UIView Animations

2019-04-17 10:31发布

问题:

I am trying to perform multiple UIView animations one after the other. However, I've heard that it's bad practice to perform multiple UIView animations one after the other, and that I should instead use Core Animation. I tried this code:

//First Animation
[UIView beginAnimations:@"animation1" context:nil];
[UIView setAnimationDuration:2];
nwView.backgroundColor = [UIColor redColor];
nwView.frame = CGRectMake(CGRectGetMidX(screenSize),
                          CGRectGetMinY(screenSize),
                          width, 
                          height);
nwView.transform = CGAffineTransformMakeRotation(45.0f);
[UIView commitAnimations];

//Second Animation
[UIView beginAnimations:@"second animation" context:nil];
[UIView setAnimationDuration:2];

nwView.transform = CGAffineTransformMakeScale(0.5, 0.33);
nwView.backgroundColor = [UIColor purpleColor];

[UIView commitAnimations];

But it only does the second animation. I know this question is similar to UIView two animations coexisting, but it has a slightly different context.

回答1:

I don't think there is anything wrong with doing 2 animations in a row using UIView blocks. Just make sure you start your second animation in the completino block of the first animation.

Without blocks (your example) it is not working as you will have to set a delegate to the animation or set a selector for setAnimationDidStopSelector. There you should start the second animation.

But again, nothing wrong in doing animations with blocks (it is the preferred way).



回答2:

If you are looking to do multiple animations right after another this is not the way to do it. The code you posted with execute at almost the same time, meaning the animations would be performed at about the same time.

Instead what you should do is set the delegate for the first animation, and then do the first animation. Then when the animationDidStop method is called for the first animation, you should do the second animation. This makes sure they are one after another.

This is how you would do it, assuming you call doMyAnimations to start the animation.

-(void)doMyAnimations{
    //First Animation
    [UIView beginAnimations:@"animation1" context:nil];
    [UIView setAnimationDuration:2];
    [UIView setAnimationDelegate:self];
    nwView.backgroundColor = [UIColor redColor];
    nwView.frame = CGRectMake(CGRectGetMidX(screenSize),
                              CGRectGetMinY(screenSize),
                              width, 
                              height);
    nwView.transform = CGAffineTransformMakeRotation(45.0f);
    [UIView commitAnimations];
}

- (void)animationWillStart:(NSString *)animationID context:(void *)context{
}

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
    if([animationID isEqualToString:@"animation1"]){
        //Second Animation
        [UIView beginAnimations:@"second animation" context:nil];
        [UIView setAnimationDuration:2];

        nwView.transform = CGAffineTransformMakeScale(0.5, 0.33);
        nwView.backgroundColor = [UIColor purpleColor];

        [UIView commitAnimations];
    }
}

Keep in mind that the nwView would have to be accessible throughout the entire class. If it isn't you can either make it an instance variable, or find another way to get access to it in the animationDidStop method.



回答3:

You can use blocks for this purpose and get a very clean result.

NSMutableArray* animationBlocks = [NSMutableArray new];

typedef void(^animationBlock)(BOOL);

// getNextAnimation
// removes the first block in the queue and returns it
animationBlock (^getNextAnimation)() = ^{
    animationBlock block = (animationBlock)[animationBlocks firstObject];
    if (block){
        [animationBlocks removeObjectAtIndex:0];
        return block;
    }else{
         return ^(BOOL finished){};
    }
};

//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
    [UIView animateWithDuration:1.0 animations:^{
        //...animation code...
    } completion: getNextAnimation()];
}];

//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
    [UIView animateWithDuration:1.0 animations:^{
        //...animation code...
    } completion: getNextAnimation()];
}];

//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
    NSLog(@"Multi-step Animation Complete!");
}];

// execute the first block in the queue
getNextAnimation()(YES);

Taken from: http://xibxor.com/objective-c/uiview-animation-without-nested-hell/