Reputation: 4486
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
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
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
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
Reputation: 28767
In the completion handler of the first animation, start the second one.
Upvotes: 0
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