iphonecore-animationscalecakeyframeanimation

How Do You CAKeyframeAnimation Scale?


I want to create an animation with several key frames. I want my Layer (a button in this case) to scale up to 1.5 then down to 0.5 then up to 1.2 then down to 0.8 then 1.0.

I also want to EaseIn and EaseOut of each keyframe.

As you can imagine, this will create a Springy/Bounce effect on the spot.

In other parts of my app I have been using CAKeyframeAnimation like this (see below code). This creates a similar springy animation but for x and y position.

Can I adapt the below code to affect scale instead of position?

Thank you in advance!

- (CAAnimation*)monInAnimation {
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path,NULL,113,320);
CGPathAddLineToPoint(path, NULL, 113.5, 283);
CGPathAddLineToPoint(path, NULL, 113.5, 179);
CGPathAddLineToPoint(path, NULL, 113.5, 207);  
CGPathAddLineToPoint(path, NULL, 113.5, 187);
CGPathAddLineToPoint(path, NULL, 113.5, 199);
CGPathAddLineToPoint(path, NULL, 113.5, 193);
CGPathAddLineToPoint(path, NULL, 113.5, 195);
CGPathAddLineToPoint(path, NULL, 113.5, 194);

CAKeyframeAnimation *
animation = [CAKeyframeAnimation 
             animationWithKeyPath:@"position"];

[animation setPath:path];
[animation setDuration:1.5];
[animation setCalculationMode:kCAAnimationLinear];
NSArray *arr = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0], 
 [NSNumber numberWithFloat:0.12], 
 [NSNumber numberWithFloat:0.24], 
 [NSNumber numberWithFloat:0.36], 
 [NSNumber numberWithFloat:0.48],
 [NSNumber numberWithFloat:0.60],
 [NSNumber numberWithFloat:0.72],
 [NSNumber numberWithFloat:0.84],
 [NSNumber numberWithFloat:1.0],nil];

 [animation setKeyTimes:arr];
 [animation setTimingFunctions:[NSArray arrayWithObjects:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
                                [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut],
                                [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], nil]];
 //[animation setAutoreverses:YES];
CFRelease(path);
return animation;

}

- (void)monBtnIn {

[monButton.layer setPosition:CGPointMake(113.5,194)];
[monButton.layer addAnimation:[self monInAnimation] 
                    forKey:@"position"];

}


Solution

  • Two alternative solutions for you:

    First, You can also animate the transform property.

    Using Brad's code, but using @"transform" for the keypath. The primary advantage being that you do not have to calculate the actual frame, but instead provide a simple scaling factor:

    Objective-C:

    CAKeyframeAnimation *boundsOvershootAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
    
    CATransform3D startingScale = CATransform3DScale (layer.transform, 0, 0, 0);
    CATransform3D overshootScale = CATransform3DScale (layer.transform, 1.2, 1.2, 1.0);
    CATransform3D undershootScale = CATransform3DScale (layer.transform, 0.9, 0.9, 1.0);
    CATransform3D endingScale = layer.transform; 
    
    NSArray *boundsValues = [NSArray arrayWithObjects:[NSValue valueWithCATransform3D:startingScale],
                                                      [NSValue valueWithCATransform3D:overshootScale],
                                                      [NSValue valueWithCATransform3D:undershootScale],
                                                      [NSValue valueWithCATransform3D:endingScale], nil];
    [boundsOvershootAnimation setValues:boundsValues];
    
    NSArray *times = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0f],
                      [NSNumber numberWithFloat:0.5f],
                      [NSNumber numberWithFloat:0.9f],
                      [NSNumber numberWithFloat:1.0f], nil];    
    [boundsOvershootAnimation setKeyTimes:times];
    
    
    NSArray *timingFunctions = [NSArray arrayWithObjects:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut], 
                                [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                nil];
    [boundsOvershootAnimation setTimingFunctions:timingFunctions];
    boundsOvershootAnimation.fillMode = kCAFillModeForwards;
    boundsOvershootAnimation.removedOnCompletion = NO;
    

    Swift 4:

    let boundsOvershootAnimation = CAKeyframeAnimation(keyPath: "transform")
    
    let startingScale = CATransform3DScale(layer.transform, 0, 0, 0)
    let overshootScale = CATransform3DScale(layer.transform, 1.2, 1.2, 1.0)
    let undershootScale = CATransform3DScale(layer.transform, 0.9, 0.9, 1.0)
    let endingScale = layer.transform
    
    boundsOvershootAnimation.values = [startingScale, overshootScale, undershootScale, endingScale]
    
    boundsOvershootAnimation.keyTimes = [0.0, 0.5, 0.9, 1.0].map { NSNumber(value: $0) }
    
    boundsOvershootAnimation.timingFunctions = [
        CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseOut),
        CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut),
        CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut)
    ]
    
    boundsOvershootAnimation.fillMode = kCAFillModeForwards
    boundsOvershootAnimation.isRemovedOnCompletion = false
    

    Second, and likely easier, is using FTUtils, an open source wrapper for core animation. It includes a stock "springy" animation.

    You can get it at: http://github.com/neror/ftutils