combine multiple CAAnimation sequentially - serialization

I was reading about CATransactions and then thought this might help solve my problem.
This is what I wan't to do:
I have 3 animations in the same layer, which all have their own duration. I create the animations using CAKeyframeAnimation with a CGMutablePathRef.
say for example:
anim1 -> duration 5s
anim2 -> 3s
anim3 -> 10s
Now I want to serialize them sequentially. I tried to use CAAnimationGroup but the animations run concurrently.
I read about CATransaction, is it a possible solution? Can you give me a little example?
thanks for help !

If by serialization you mean starting each animation after the previous one has finished, use the beginTime property (defined in CAMediaTiming protocol). Note that its documentation is a little misleading. Here's an example:
anim2.beginTime = anim1.beginTime + anim1.duration;
anim3.beginTime = anim2.beginTime + anim2.duration;

If You are sure to do this thing with Layers then you may try like following
Using Completion Block in CATransactions
-(void)animateThreeAnimationsOnLayer:(CALayer*)layer animation:(CABasicAnimation*)firstOne animation:(CABasicAnimation*)secondOne animation:(CABasicAnimation*)thirdOne{
[CATransaction begin];
[CATransaction setCompletionBlock:^{
[CATransaction begin];
[CATransaction setCompletionBlock:^{
[CATransaction begin];
[CATransaction setCompletionBlock:^{
//If any thing on completion of all animations
}];
[layer addAnimation:thirdOne forKey:#"thirdAnimation"];
[CATransaction commit];
}];
[layer addAnimation:secondOne forKey:#"secondAnimation"];
[CATransaction commit];
}];
[layer addAnimation:firstOne forKey:#"firstAnimation"];
[CATransaction commit];
}
Another way is by applying delay to begin animation.
-(void)animateThreeAnimation:(CALayer*)layer animation:(CABasicAnimation*)firstOne animation:(CABasicAnimation*)secondOne animation:(CABasicAnimation*)thirdOne{
firstOne.beginTime=0.0;
secondOne.beginTime=firstOne.duration;
thirdOne.beginTime=firstOne.duration+secondOne.duration;
[layer addAnimation:firstOne forKey:#"firstAnim"];
[layer addAnimation:secondOne forKey:#"secondAnim"];
[layer addAnimation:thirdOne forKey:#"thirdAnim"];
}
And if You are going to use UIVIew Animation
//if View is applicable in your requirement then you can look this one;
-(void)animateThreeAnimationOnView{
[UIView animateWithDuration:2.0 animations:^{
//first Animation
} completion:^(BOOL finished) {
[UIView animateWithDuration:2.0 animations:^{
//Second Animation
} completion:^(BOOL finished) {
[UIView animateWithDuration:2.0 animations:^{
//Third Animation
}];
}];
}];
}

Related

CABasicAnimation comleted immediately after CALayer "setFrame", why?

-(void)startAnimationWithTime:(CGFloat)animationTime
fromPoint:(CGPoint)fromPoint
toPoint:(CGPoint)toPoint
Completion:(SYBDanmakuAnimationCompletion)completion
{
[CATransaction begin];
[CATransaction setDisableActions:YES];
[CATransaction setCompletionBlock:^{
completion(YES);
}];
CABasicAnimation *mover = [CABasicAnimation animationWithKeyPath:#"position"];
[mover setDuration:animationTime];
[mover setFromValue:[NSValue valueWithCGPoint:fromPoint]];
[mover setToValue:[NSValue valueWithCGPoint:toPoint]];
[self addAnimation:mover forKey:#"move"];
[CATransaction commit];
}
This is the code of myLayer ,when I change the frame of its superLayer , this animation will complete immediately, who can tell me why? I want it to work correctly.

Run animation after addLineToPoint for UIBezier path

I am trying to force one animation waiting for the other, but without luck.
UIBezierPath *path = [UIBezierPath bezierPath];
This is what I want to do:
[path addLineToPoint: point1];
and when this is done call this:
imageview1.transform = CGAffineTransformMakeScale(1.5f, 1.5f);
While I'm still unclear as to your needs, here is how you can call one animation AFTER the first one finishes:
[UIView animateWithDuration:1.0f
delay:0.0f
options:UIViewAnimationOptionCurveLinear
animations:^(void){
// Add in your first chunk of animated code.
}
completion:^(BOOL finished) {
[UIView animateWithDuration:1.0f
delay:0.0f
options:UIViewAnimationOptionCurveLinear
animations:^(void){
// Add in your second chunk of animated code.
}
completion:^(BOOL finished) {
}];
}];
Hope that Helps!

Difficulty chaining two UIView animations

Here's what I'm trying to accomplish.
A UILabel will appear on screen at 100% alpha and move up 80pts using a UIView animation. As soon as that animation ends, I want it to continue 80pts higher while fading out to alpha 0. I want these two animations to appear as one seamless animation.
I thought maybe I could do this with a UIView animation and then put a second UIView animation in the completion block as shown below. However, there appears to be a delay before the completion block is executed which prevents the two animations from appearing seamless.
Can anyone tell me the best way to do what I'm trying to do?
Thanks!
[UIView animateWithDuration:1.2 delay:0 options:UIViewAnimationCurveLinear animations:^{
myLabel.center = endPoint;
}
completion:^(BOOL finished) {
[UIView animateWithDuration:1.2 delay:0 options:UIViewAnimationCurveLinear animations:^{
myLabel.center = endPoint2;
myLabel.alpha = 0;
}
completion:^(BOOL finished) {
NSLog(#"animations complete");
}];
}];
This should work. I see no reason why not. Just a small addition, check for animation completion in finished block. As this will take care of any errors during animation. Also if you are planning to make this UILabel tappable then you need to make it so by setting the flag UIViewAnimationOptionAllowUserInteraction.
[UIView animateWithDuration:1.2 delay:0 options:UIViewAnimationCurveLinear | UIViewAnimationOptionAllowUserInteraction animations:^{
myLabel.center = endPoint;
myLabel.alpha = 1;
}
completion:^(BOOL finished) {
if(finished){
[UIView animateWithDuration:1.2 delay:0 options:UIViewAnimationCurveLinear | UIViewAnimationOptionAllowUserInteraction animations:^{
myLabel.center = endPoint2;
myLabel.alpha = 0;
}
completion:^(BOOL finished) {
NSLog(#"animations complete");
}];
}
}];

Triggering a method after a delay without abusing [UIView animateWithDuration]?

I have a UIViewController that should control 2 UIImageViews to fade in.
I managed to animate them using the UIView animateWithDuration method, like this:
[UIView animateWithDuration:1.5
animations:^{
[mFrontpageTitle setAlpha:0];
[mFrontpageTitle setAlpha:1];
}
completion:^(BOOL finished){
[self AnimatePause];
}];
and
-(void)AnimatePause {
[UIView animateWithDuration:5.0
animations:^{
[mFrontpageTitle setAlpha:0.99];
[mFrontpageTitle setAlpha:1];
}
completion:^(BOOL finished){
[self AnimateAuthor];
}];
which fires the next animation. Now, creating a transition from .99 to 1.0 is not very clean.
Is there a better way to trigger a block or sending a message by just defining a duration?
thanks
Zuppa
NSTimeInterval delay = 1.5; //in seconds
[self performSelector:#selector(myMethod) withObject:nil afterDelay:delay];

Why is my CATransaction not honoring the duration I set?

I'm porting an iPhone app to Mac OS X. This code was being used successfully on the iPhone:
- (void) moveTiles:(NSArray*)tilesToMove {
[UIView beginAnimations:#"tileMovement" context:nil];
[UIView setAnimationDuration:0.1];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(tilesStoppedMoving:finished:context:)];
for( NSNumber* aNumber in tilesToMove ) {
int tileNumber = [aNumber intValue];
UIView* aView = [self viewWithTag:tileNumber];
aView.frame = [self makeRectForTile:tileNumber];
}
[UIView commitAnimations];
}
The Mac version uses CATransaction to group the animations, like so:
- (void) moveTiles:(NSArray*)tilesToMove {
[CATransaction begin];
[CATransaction setAnimationDuration:0.1];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[CATransaction setCompletionBlock:^{
[gameDelegate tilesMoved];
}];
for( NSNumber* aNumber in tilesToMove ) {
int tileNumber = [aNumber intValue];
NSView* aView = [self viewWithTag:tileNumber];
[[aView animator] setFrame:[self makeRectForTile:tileNumber]];
}
[CATransaction commit];
}
The animation is executing fine, except that the duration is 1.0 seconds. I can change the setAnimationDuration: call to anything, or omit it completely, and still the animation is 1.0 seconds in duration, every time. I also don't think the setAnimationTimingFunction: call is doing anything. However, setCompletionBlock: is working, because that block is executing when the animation completes.
What am I doing wrong here?
If I am not mistaken you cannot use CoreAnimation to animate NSView's directly. For that you need NSAnimationContext and [NSView animator]. CATransaction will only work with CALayers.
It doesn't answer the question exactly, but I ended up using NSAnimationContext instead of CATransaction.
- (void) moveTiles:(NSArray*)tilesToMove {
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.1f];
for( NSNumber* aNumber in tilesToMove ) {
int tileNumber = [aNumber intValue];
NSView* aView = [self viewWithTag:tileNumber];
[[aView animator] setFrame:[self makeRectForTile:tileNumber]];
CAAnimation *animation = [aView animationForKey:#"frameOrigin"];
animation.delegate = self;
}
[NSAnimationContext endGrouping];
}
It's works, but I'm not terribly happy about it. Mainly, NSAnimationContext doesn't have a callback completion mechanism like CATransaction does, so I had to put the thing there to explicitly get the view's animation and set the delegate so a callback gets triggered. Problem with that is, it gets triggered multiple times for each animation. This turns out to have no ill effects for what I'm doing, it just feels wrong.
This is workable, but if anyone knows a better solution, I'd still like one.