Showing posts with label CAAnimation. Show all posts
Showing posts with label CAAnimation. Show all posts

Wednesday, June 12, 2013

CAAnimation animationDidStop finished example in Objective C (iOS).

CAAnimation animationDidStop finished

Called when the animation completes its active duration or is removed from the object it is attached to.

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag

Parameters of [CAAnimation animationDidStop finished]
theAnimation
The CAAnimation instance that stopped animating.
flag
If YES, the animation reached the end of its active duration without being removed.

CAAnimation animationDidStop finished example.
I created a typedef for a block:

typedef void (^animationCompletionBlock)(void);
And a key that I use to add a block to an animation:

#define kAnimationCompletionBlock @"animationCompletionBlock"
Then, if I want to run animation completion code after a CAAnimation finishes, I set myself as the delegate of the animation, and add a block of code to the animation using setValue:forKey:

animationCompletionBlock theBlock = ^void(void)
{
  //Code to execute after the animation completes goes here   
};
[theAnimation setValue: theBlock forKey: kAnimationCompletionBlock];
Then, I implement an animationDidStop:finished: method, that checks for a block at the specified key and executes it if found:

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
  animationCompletionBlock theBlock = [theAnimation valueForKey: kAnimationCompletionBlock];
  if (theBlock)
    theBlock();
}

Example of [CAAnimation animationDidStop finished].
You can set key/value objects for CAAnimation instance like this:

CABasicAnimation *theAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
[theAnimation setValue:@"animation1" forKey:@"id"];
theAnimation.delegate = self;

CABasicAnimation *theAnimation2 = [CABasicAnimation animationWithKeyPath:@"opacity"];
[theAnimation2 setValue:@"animation2" forKey:@"id"];   
theAnimation2.delegate = self;
Check which one was called in delegate method:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
    if([[anim valueForKey:@"id"] isEqual:@"animation1"]) {
        NSLog(@"animation1");
    }
    if([[anim valueForKey:@"id"] isEqual:@"animation2"]) {
        NSLog(@"animation2");
    }
}

CAAnimation animationDidStop finished example.
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
//NSLog(@"First Animation stopped");

if (anim ==[topHalfCharLayerFront animationForKey:@"topCharAnim"]) {
    NSLog(@"Top Animation is: %@", anim);
    topHalfCharLayerFront.hidden = YES;
    [bottomHalfCharLayerFront addAnimation:[self bottomCharFlap:bottomHalfCharLayerFront] forKey:@"bottomCharAnim"];
    bottomHalfCharLayerFront.hidden = NO;
}

else if ((anim ==[bottomHalfCharLayerFront animationForKey:@"bottomCharAnim"])) {

    NSLog(@"Bottom Animation is: %@", anim);

}

End of CAAnimation animationDidStop finished example article.

CAAnimation animationDidStop example in Objective C (iOS).

CAAnimation animationDidStop

Called when the animation completes its active duration or is removed from the object it is attached to.

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag

Parameters of [CAAnimation animationDidStop]
theAnimation
The CAAnimation instance that stopped animating.
flag
If YES, the animation reached the end of its active duration without being removed.

CAAnimation animationDidStop example.
I created a typedef for a block:

typedef void (^animationCompletionBlock)(void);
And a key that I use to add a block to an animation:

#define kAnimationCompletionBlock @"animationCompletionBlock"
Then, if I want to run animation completion code after a CAAnimation finishes, I set myself as the delegate of the animation, and add a block of code to the animation using setValue:forKey:

animationCompletionBlock theBlock = ^void(void)
{
  //Code to execute after the animation completes goes here   
};
[theAnimation setValue: theBlock forKey: kAnimationCompletionBlock];
Then, I implement an animationDidStop:finished: method, that checks for a block at the specified key and executes it if found:

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
  animationCompletionBlock theBlock = [theAnimation valueForKey: kAnimationCompletionBlock];
  if (theBlock)
    theBlock();
}

Example of [CAAnimation animationDidStop].
You can set key/value objects for CAAnimation instance like this:

CABasicAnimation *theAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
[theAnimation setValue:@"animation1" forKey:@"id"];
theAnimation.delegate = self;

CABasicAnimation *theAnimation2 = [CABasicAnimation animationWithKeyPath:@"opacity"];
[theAnimation2 setValue:@"animation2" forKey:@"id"];   
theAnimation2.delegate = self;
Check which one was called in delegate method:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
    if([[anim valueForKey:@"id"] isEqual:@"animation1"]) {
        NSLog(@"animation1");
    }
    if([[anim valueForKey:@"id"] isEqual:@"animation2"]) {
        NSLog(@"animation2");
    }
}

CAAnimation animationDidStop example.
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
//NSLog(@"First Animation stopped");

if (anim ==[topHalfCharLayerFront animationForKey:@"topCharAnim"]) {
    NSLog(@"Top Animation is: %@", anim);
    topHalfCharLayerFront.hidden = YES;
    [bottomHalfCharLayerFront addAnimation:[self bottomCharFlap:bottomHalfCharLayerFront] forKey:@"bottomCharAnim"];
    bottomHalfCharLayerFront.hidden = NO;
}

else if ((anim ==[bottomHalfCharLayerFront animationForKey:@"bottomCharAnim"])) {

    NSLog(@"Bottom Animation is: %@", anim);

}

End of CAAnimation animationDidStop example article.

CAAnimation animationDidStart example in Objective C (iOS).

CAAnimation animationDidStart

Called when the animation begins its active duration.

- (void)animationDidStart:(CAAnimation *)theAnimation

Parameters of [CAAnimation animationDidStart]
theAnimation
The CAAnimation instance that started animating.

CAAnimation animationDidStart example.
//--------------------------------------------------------------------------------------------------

#pragma mark - CAAnimation Delegate Methods

//--------------------------------------------------------------------------------------------------

- (void)animationDidStart:(CAAnimation *)theAnimation
{
    [CATransaction begin];
    [CATransaction setValue:(id)kCFBooleanTrue
                     forKey:kCATransactionDisableActions];

    // Set the layer's target position here.       

    [CATransaction commit];
}

Example of [CAAnimation animationDidStart].
What's happening is that you're seeing the animation in the presentation layer. However, that doesn't update the actual position of your layer. So, once the animation finishes, you see the layer as it was because it hasn't changed.

It's really worth reading the "Core Animation Rendering Architecture". Otherwise this can be very confusing.

To fix it, set a delegate to your CABasicAnimation as follows:

[animation setDelegate:self];

Then, create a method to set your target properties that you want when the animation completes. Now, here's the confusing part. You should do this on animationDidStart not animationDidStop. Otherwise, the presentation layer animation will finish, and you'll get a flicker as you see the calayer in the original position then it jumps - without animation - to the target position. Try it with animationDidStop and you'll see what I mean.

I hope that's not too confusing!

- (void)animationDidStart:(CAAnimation *)theAnimation
{
    [calayer setWhateverPropertiesExpected];
}

End of CAAnimation animationDidStart example article.

CAAnimation setTimingFunction example in Objective C (iOS).

CAAnimation setTimingFunction

An optional timing function defining the pacing of the animation.

@property(retain) CAMediaTimingFunction *timingFunction

Discussion of [CAAnimation setTimingFunction]
Defaults to nil, indicating linear pacing.

CAAnimation setTimingFunction example.
When animating the movement of a UIView within a begin / commit animation block, you can use the following method to set the animation timing curve:

[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
When animating a layer via CAAnimation, you can use the following to set the timing function there:

[animation setTimingFunction:kCAMediaTimingFunctionEaseInEaseOut];
Finally, when wrapping a series of animations in a CATransaction, you can use the following to set the coordinated timing function of all the animations:

[CATransaction setAnimationTimingFunction:kCAMediaTimingFunctionEaseInEaseOut];

Example of [CAAnimation setTimingFunction].
-(void)animateCounterMoveFor:(int)playerType counterId:(int)countId {

    // set up the move to an end point
    CABasicAnimation *move = [CABasicAnimation animationWithKeyPath:@"position"];
    [move setDuration:3.5];
    [move setToValue:[NSValue valueWithCGPoint:CGPointMake((xPos), (yPos))]];
    [move setFillMode:kCAFillModeForwards];
    [move setRemovedOnCompletion:NO];
    CAMediaTimingFunction *tf = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    [move setTimingFunction:tf];

    [layer addAnimation:move forKey:@"position"];   // Replaces the previous position animation on the layer
    [layer setPosition:CGPointMake(xPos, yPos)];        // Sets the final position after animation is finished

    // enable the animation stop to set layer to right position
    [move setValue:[NSNumber numberWithInt:countId] forKey:@"counterId"];
    [move setValue:[NSNumber numberWithInt:playerType] forKey:@"playerType"];
    [move setDelegate:self];

}

CAAnimation setTimingFunction example.
[myLayer addAnimation:frameAnimation forKey:@"frame"];
You may also set an action to the layer to make all frame changes animated with your animation:

CABasicAnimation *frameAnimation = [CABasicAnimation animation];
frameAnimation.duration = 2.5;
frameAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

myLayer.actions = [NSDictionary dictionaryWithObjectsAndKeys:frameAnimation, @"frame", nil];

End of CAAnimation setTimingFunction example article.

CAAnimation timingFunction example in Objective C (iOS).

CAAnimation timingFunction

An optional timing function defining the pacing of the animation.

@property(retain) CAMediaTimingFunction *timingFunction

Discussion of [CAAnimation timingFunction]
Defaults to nil, indicating linear pacing.

CAAnimation timingFunction example.
When animating the movement of a UIView within a begin / commit animation block, you can use the following method to set the animation timing curve:

[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
When animating a layer via CAAnimation, you can use the following to set the timing function there:

[animation setTimingFunction:kCAMediaTimingFunctionEaseInEaseOut];
Finally, when wrapping a series of animations in a CATransaction, you can use the following to set the coordinated timing function of all the animations:

[CATransaction setAnimationTimingFunction:kCAMediaTimingFunctionEaseInEaseOut];

Example of [CAAnimation timingFunction].
- (void)viewDidLoad {
    UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 50.0, 50.0)];

    v.backgroundColor = [UIColor redColor];
    CGFloat y = self.view.bounds.size.height;
    v.center = CGPointMake(self.view.bounds.size.width/2.0, 50.0/2.0);
    [self.view addSubview:v];

    //[CATransaction begin];

    CAKeyframeAnimation * animation;
    animation = [CAKeyframeAnimation animationWithKeyPath:@"position.y"];
    animation.duration = 3.0;
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;

    NSMutableArray *values = [NSMutableArray array];
    NSMutableArray *timings = [NSMutableArray array];
    NSMutableArray *keytimes = [NSMutableArray array];

    //Start
    [values addObject:[NSNumber numberWithFloat:25.0]];
    [timings addObject:GetTiming(kCAMediaTimingFunctionEaseIn)];
    [keytimes addObject:[NSNumber numberWithFloat:0.0]];

    //Drop down
    [values addObject:[NSNumber numberWithFloat:y]];
    [timings addObject:GetTiming(kCAMediaTimingFunctionEaseOut)];
    [keytimes addObject:[NSNumber numberWithFloat:0.6]];

    // bounce up
    [values addObject:[NSNumber numberWithFloat:0.7 * y]];
    [timings addObject:GetTiming(kCAMediaTimingFunctionEaseIn)];
    [keytimes addObject:[NSNumber numberWithFloat:0.8]];

    // fihish down
    [values addObject:[NSNumber numberWithFloat:y]];
    [keytimes addObject:[NSNumber numberWithFloat:1.0]];
    //[timings addObject:GetTiming(kCAMediaTimingFunctionEaseIn)];


    animation.values = values;
    animation.timingFunctions = timings;
    animation.keyTimes = keytimes;

    [v.layer addAnimation:animation forKey:nil];  

    //[CATransaction commit];

}

CAAnimation timingFunction example.
[myLayer addAnimation:frameAnimation forKey:@"frame"];
You may also set an action to the layer to make all frame changes animated with your animation:

CABasicAnimation *frameAnimation = [CABasicAnimation animation];
frameAnimation.duration = 2.5;
frameAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

myLayer.actions = [NSDictionary dictionaryWithObjectsAndKeys:frameAnimation, @"frame", nil];

End of CAAnimation timingFunction example article.

CAAnimation setRemovedOnCompletion example in Objective C (iOS).

CAAnimation setRemovedOnCompletion

Determines if the animation is removed from the target layer’s animations upon completion.

@property(getter=isRemovedOnCompletion) BOOL removedOnCompletion

Discussion of [CAAnimation setRemovedOnCompletion]
When YES, the animation is removed from the target layer’s animations once its active duration has passed. Defaults to YES.

CAAnimation setRemovedOnCompletion example.
CABasicAnimation *rotationAnimation =[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; //Rotate about z-axis
[rotationAnimation setFromValue:[NSNumber numberWithFloat:fromDegree]];
[rotationAnimation setToValue:[NSNumber numberWithFloat:toDegree]];
[rotationAnimation setDuration:0.2];
[rotationAnimation setRemovedOnCompletion:NO];
[rotationAnimation setFillMode:kCAFillModeForwards];

Example of [CAAnimation setRemovedOnCompletion].
        const CFTimeInterval DURATION = 2.0f;
        CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
        [animation setDuration:DURATION];
        [animation setRemovedOnCompletion:NO];
        [animation setFillMode:kCAFillModeForwards];   
        [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
        [animation setFromValue:[NSNumber numberWithDouble:400.0]];
        [animation setToValue:[NSNumber numberWithDouble:0.0]];
        [animation setRepeatCount:1.0];
        [animation setDelegate:self];
        [myview.layer addAnimation:animation forKey:@"animatePositionY"];

        animation = [CABasicAnimation animationWithKeyPath:@"position.x"];
        [animation setDuration:DURATION];
        [animation setRemovedOnCompletion:NO];
        [animation setFillMode:kCAFillModeForwards];   
        [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
        [animation setFromValue:[NSNumber numberWithDouble:300.0]];
        [animation setToValue:[NSNumber numberWithDouble:0.0]];
        [animation setRepeatCount:1.0];
        [animation setDelegate:self];
        [myview.layer addAnimation:animation forKey:@"animatePositionX"];

CAAnimation setRemovedOnCompletion example.
- (void)aniFromColor:(CGColorRef)fromCol toColor:(CGColorRef)toCol {
    FlipLayer3View *v = (FlipLayer3View *)[self view];
    const CGFloat *fromColors = CGColorGetComponents(fromCol);
    const CGFloat *toColors = CGColorGetComponents(toCol);

    v.theSquare.backgroundColor = fromCol;

    ani1 = [[CABasicAnimation animationWithKeyPath:@"transform"] retain];
    [ani1 setDuration:dur];
    [ani1 setToValue:[NSValue valueWithCATransform3D:CATransform3DConcat(v.theSquare.transform, CATransform3DRotate(CATransform3DIdentity, M_PI/2, -1, 1, 0))]];
    [ani1 setValue:@"shrink" forKey:@"name"];
    [ani1 setDelegate:self];
    [ani1 setFillMode:kCAFillModeForwards];
    [ani1 setRemovedOnCompletion:NO];

    ani2 = [[CABasicAnimation animationWithKeyPath:@"backgroundColor"] retain];
    UIColor *c1 = [UIColor colorWithRed:(fromColors[0]/2.0f) green:(fromColors[1]/2.0f) blue:(fromColors[2]/2.0f) alpha:1.0f];

    const float *cgCol1 = CGColorGetComponents(fromCol);
    const float *cgCol2 = CGColorGetComponents(c1.CGColor);
    NSLog(@"Setting color FROM R:%f G:%f B:%f", cgCol1[0], cgCol1[1], cgCol1[2]);
    NSLog(@"Setting color TO R:%f G:%f B:%f", cgCol2[0], cgCol2[1], cgCol2[2]);

    [ani2 setDuration:dur];
    [ani2 setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
    [ani2 setFromValue:(id)fromCol];
    [ani2 setToValue:(id)c1.CGColor];
    [ani2 setValue:@"dim" forKey:@"name"];
    [ani2 setDelegate:self];
    [ani2 setFillMode:kCAFillModeForwards];
    [ani2 setRemovedOnCompletion:NO];

    ani3 = [[CABasicAnimation animationWithKeyPath:@"transform"] retain];
    [ani3 setDuration:dur];
    [ani3 setFromValue:[NSValue valueWithCATransform3D:CATransform3DConcat(v.theSquare.transform, CATransform3DRotate(CATransform3DIdentity, M_PI/2, -1, 1, 0))]];
    [ani3 setToValue:[NSValue valueWithCATransform3D:v.theSquare.transform]];
    [ani3 setValue:@"grow" forKey:@"name"]; [ani3 setDelegate:self];
    [ani3 setFillMode:kCAFillModeForwards];
    [ani3 setRemovedOnCompletion:NO];

    ani4 = [[CABasicAnimation animationWithKeyPath:@"backgroundColor"] retain];
    UIColor *c2 = [UIColor colorWithRed:(toColors[0]/2.0f) green:(toColors[1]/2.0f) blue:(toColors[2]/2.0f) alpha:1.0f];

    cgCol1 = CGColorGetComponents(c2.CGColor);
    cgCol2 = CGColorGetComponents(toCol);
    NSLog(@"Setting color FROM R:%f G:%f B:%f", cgCol1[0], cgCol1[1], cgCol1[2]);
    NSLog(@"Setting color TO R:%f G:%f B:%f", cgCol2[0], cgCol2[1], cgCol2[2]);

    [ani4 setDuration:dur];
    [ani4 setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
    [ani4 setFromValue:(id)c2.CGColor];
    [ani4 setToValue:(id)toCol];
    [ani4 setValue:@"glow" forKey:@"name"]; [ani4 setDelegate:self];
    [ani4 setFillMode:kCAFillModeForwards];
    [ani4 setRemovedOnCompletion:NO];

    [v.theSquare addAnimation:ani1 forKey:@"rotate1"];
    [v.theSquare addAnimation:ani2 forKey:@"fadeout"];

}

End of CAAnimation setRemovedOnCompletion example article.

CAAnimation removedOnCompletion example in Objective C (iOS).

CAAnimation removedOnCompletion

Determines if the animation is removed from the target layer’s animations upon completion.

@property(getter=isRemovedOnCompletion) BOOL removedOnCompletion

Discussion of [CAAnimation removedOnCompletion]
When YES, the animation is removed from the target layer’s animations once its active duration has passed. Defaults to YES.

CAAnimation removedOnCompletion example.
rota.removedOnCompletion = NO;
That should leave the presentation layer where it was when the animation finished. The default is YES, which will snap back to the model value, i.e., the behavior you describe.

The fillMode should also be set, i.e.,

rota.fillMode = kCAFillModeForwards;

Example of [CAAnimation removedOnCompletion].
They cause the presentation to be preserved at the end of the animation.

animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;

Then the "animationDidStop:" method can be used to remove the view at the end of the animation:

-(void) animationDidStop:(CAAnimation *) animation finished:(bool) flag {
    if (animation == [containerView.layer animationForKey:@"moveX"]) {
        // remove view here, add another view and/or start another transition
    }
}

CAAnimation removedOnCompletion example.
An example would be

CABasicAnimation *scaleX = [CABasicAnimation animationWithKeyPath:@"transform.scale.x"];
//this is not used, as the group provides the duration
scaleX.duration = duration;
scaleX.autoreverses = NO;

scaleX.toValue = [NSNumber numberWithFloat:1.0];
scaleX.fromValue = [NSNumber numberWithFloat:scaleFrom];
scaleX.fillMode = kCAFillModeForwards;
scaleX.removedOnCompletion = NO;

CABasicAnimation *scaleY = [CABasicAnimation animationWithKeyPath:@"transform.scale.y"];
//this is not used, as the group provides the duration
scaleY.duration = duration;
scaleY.autoreverses = NO;

scaleY.toValue = [NSNumber numberWithFloat:1.0];
scaleY.fromValue = [NSNumber numberWithFloat:scaleFrom];
scaleY.fillMode = kCAFillModeForwards;
scaleY.removedOnCompletion = NO;

//add in the translation animations

NSMutableArray* animationsArray = [NSMutableArray arrayWithObjects:scaleX,
                                                                   scaleY,
//and any other animations you want in the group
                                       nil];

CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.duration = 1.0/2;
animationGroup.timingFunction = [CAMediaTimingFunctionfunctionWithName:kCAMediaTimingFunctionEaseIn];
animationGroup.animations = animationsArray;
animationGroup.delegate = self;
animationGroup.removedOnCompletion = YES;
animationGroup.fillMode = kCAFillModeForwards;
[animationGroup setValue:@"imageTransform" forKey:@"AnimationName"];
[b.layer addAnimation:animationGroup forKey:@"imageTransform"];

End of CAAnimation removedOnCompletion example article.