Stanley
Stanley

Reputation: 4486

The proper way of doing chain animations

void (^first_animation)();
void (^second_animation)(BOOL finished);


// First animation

first_animation = ^()
{
    g_pin_info_screen.view.alpha = 1.0;
};


// Second animation

second_animation = ^(BOOL finished)
{
    g_shadow_layer.opacity = 0.0;

    void (^set_opacity_to_1)();

    set_opacity_to_1 = ^()
    {
        g_shadow_layer.opacity = 1.0;
    };

    [UIView animateWithDuration : 2.0
            delay               : 0.0
            options             : UIViewAnimationCurveEaseInOut
            animations          : set_opacity_to_1
            completion          : nil
     ];

};



// Begin the animations

{

    float duration;

    duration = 0.35;

    [UIView animateWithDuration : duration
            delay               : 0.00
            options             : UIViewAnimationCurveEaseInOut
            animations          : first_animation
            completion          : second_animation
    ];

}

First animation executes as expected. But second animation completes but without any animation.

Hope somebody could comment on whether the above scheme is the proper way of doing this or not.

Upvotes: 3

Views: 5153

Answers (5)

Vadim Smirnov
Vadim Smirnov

Reputation: 11

Just check here: https://gist.github.com/vadimsmirnovnsk/bce345ab81a1cea25a38

You can chain it in functional style:

dispatch_block_t animationsBlock = ^{
    [self.view updateConstraintsIfNeeded];
    [self.view layoutIfNeeded];
};

[[[[[[[[[BARAnimation construct]
    initially:animationsBlock]
    animationWithDuration:0.425 animationConditions:^{
        [gridView mas_updateConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(imageView).with.offset(32.0);
        }];
    } animations:animationsBlock]
    animationWithDuration:0.425 animationConditions:^{
        [gridView mas_updateConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(imageView).with.offset(0.0);
        }];
    } animations:animationsBlock]
    animationWithDuration:0.425 animationConditions:^{
        [gridView mas_updateConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(imageView).with.offset(-32.0);
        }];
    } animations:animationsBlock]
    animationWithDuration:0.425 animationConditions:^{
        [gridView mas_updateConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(imageView).with.offset(0.0);
        }];
    } animations:animationsBlock]
    animationWithDuration:0.8 animationConditions:nil animations:^{
        foreView.alpha = 1.0;
    }]
    finally:^{
        [self.didEndSubject sendNext:[RACUnit defaultUnit]];
        [self.didEndSubject sendCompleted];
    }]
    run];

Upvotes: 1

JeffRegan
JeffRegan

Reputation: 1322

__block NSMutableArray* animationBlocks = [NSMutableArray new];
typedef void(^animationBlock)(BOOL);

// getNextAnimation
// removes the first block in the queue and returns it
animationBlock (^getNextAnimation)() = ^{

    if ([animationBlocks count] > 0){
        animationBlock block = (animationBlock)[animationBlocks objectAtIndex:0];
        [animationBlocks removeObjectAtIndex:0];
        return block;
    } else {
        return ^(BOOL finished){
            animationBlocks = nil;
        };
    }
};

[animationBlocks addObject:^(BOOL finished){
    [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
        //my first set of animations
    } completion: getNextAnimation()];
}];


[animationBlocks addObject:^(BOOL finished){
    [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
       //second set of animations
    } completion: getNextAnimation()];
}];



[animationBlocks addObject:^(BOOL finished){
    [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
        //third set
    } completion: getNextAnimation()];
}];


[animationBlocks addObject:^(BOOL finished){
    [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
        //last set of animations
    } completion:getNextAnimation()];
}];

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

Upvotes: 13

CouchDeveloper
CouchDeveloper

Reputation: 19174

With the help of the third party library there is a solution that looks like as below:

First, for convenience, define a category for UIView like so:

+(RXPromise*) rx_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations 
{
    RXPromise* promise = [RXPromise new];
    [UIView animateWithDuration:duration animations:animations: ^(BOOL finished){
         // ignore param finished here
         [promise fulfillWithValue:@"finished"]; // return just a string indicating success
    }];    
    return promise;
}

Then, define any number of asynchronous animations which execute one after the other, as follows:

[UIView rx_animateWithDuration:duration animation:^{
        ... //define first animation
    }]
.then(^id(id result){
    // ignore result, it contains the fulfill value of the promise, which is @"finished"
    return [UIView rx_animateWithDuration:duration animation:^{
        ... // define second animation
    }];
}, nil)
.then(^id(id result){
    return [UIView rx_animateWithDuration:duration animation:^{
        ...  // define third animation
    }];
}, nil)
.then(^id(id result){
    return [UIView rx_animateWithDuration:duration animation:^{
        ... // and so force
    };
}, nil);

The above statement is asynchronous!

With one line additional code you can achieve cancellation:

RXPromise* rootPromise = [UIView rx_animateWithDuration:duration animation:^{
        ... //define first animation
    }];

rootPromise.then(^id(id result){
    return [UIView rx_animateWithDuration:duration animation:^{
        ... // define second animation
    }];
}, nil)
.then(^id(id result){
    return [UIView rx_animateWithDuration:duration animation:^{
        ...  // define third animation
    }];
}, nil)
...

// later, in case you need to cancel pending animations:
[rootPromise cancel];

"RXPromise" library is available on GitHub: RXPromise. It's specifically designed for these use cases, and more. Due to full disclosure: I'm the author ;)

Upvotes: 2

AlexWien
AlexWien

Reputation: 28767

In the completion handler of the first animation, start the second one.

Upvotes: 0

SushiGrass Jacob
SushiGrass Jacob

Reputation: 19834

You need to chain them together by using + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion

Within the options: argument, you need to include UIViewAnimationOptionBeginFromCurrentState

Good luck!

Upvotes: 0

Related Questions