PrEV
Thoughts from a NeXTStep Guy on Cocoa Development

Keyframe Animation

Apr 03, 2008 by Bill Dudney

I have been doing some keyframe animations lately and figured it would make a good entry.

Keyframe animations are cool because they give you more exact control over the way your layers animate. The basic animation does a linear interpolation between the 'start' value and the 'end' value. Which works great in many if not most cases but some times you want the path your animation follows to be something different or more sophisticated. The keyframe allows you full control over the intermediate values of the animation.

Whereas the basic animation does a simple linear interpolation the keyframe allows you to specify values at intermediate times so that you can fully control the value during the whole animation. Keyframes have played a part in animation since the days of hand drawn movies. Senior artists would draw the 'key frames' for a scene and then junior artists would fill in the required frames to make the animation smooth (sometimes this process is called tweening). The keyframe animation takes care of the tweening for us we specify the 'key frames' and it does the rest.

Another cool thing is the 'value' that the keyframe can animate is anything that the basic animation can animate. Specifically that boils down to any property of types number, point, size or rect. So for example if we wanted to animate the opacity of a layer we could specify several different values (i.e. 0.25, 0.50, 0.75) that should be reached at specific times during the animation. The keyframe animation would then interpolate between each of the values that we specify.

Another aspect that is really cool is that we can use CGPath's to specify the values instead of discrete values. In other words instead of specific values at specific time points we can use a path to specify the values. For example we could animate the opacity of a layer with a bell curve, start transparent, fade up to some opacity value and then fade back. We can also use the paths to animate CGPoint and CGSize values. In this case the x value of the path becomes either the x or width value and y becomes the y or height values.

In the example we will have a layer move back and forth across the window. Here is a screen shot of the app running.

The white line is a bezier path that we are using to animate the movie layer across the screen. Its drawn simply so we can see it in greater detail. Here is the code used to create the path.

  CGMutablePathRef path = CGPathCreateMutable();

  CGPathMoveToPoint(path, NULL, _point1.x + _movieSize.width * 0.5f, _point1.y);

  CGPathAddCurveToPoint(path, NULL, _controlPoint1.x + _movieSize.width * 0.5f,

                        _controlPoint1.y, _controlPoint2.x - _movieSize.width * 0.5f, 

                        _controlPoint2.y, _point2.x - _movieSize.width * 0.5f, _point2.y);

  if(NULL != _rightBoundKeyframePath) {

    CGPathRelease(_rightBoundKeyframePath);

  }

  _rightBoundKeyframePath = CGPathCreateCopy(path);

  CGPathRelease(path);

  

  path = CGPathCreateMutable();

  CGPathMoveToPoint(path, NULL, _point2.x - _movieSize.width * 0.5f, _point2.y);

As you can see it straight Quartz path creation code. We then place that path into a keyframe animation like this;

  CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];

  animation.duration = 2.0f;

  if(YES == _atLeftEdge) {

    animation.path = _rightBoundKeyframePath;

    // the rotation mode causes the layer to rotate along with the slope of the path

    animation.rotationMode = kCAAnimationRotateAuto;

  } else {

    animation.path = _leftBoundKeyframePath;

    // since the path is reversed, rotate at 180 degrees off

    animation.rotationMode = kCAAnimationRotateAutoReverse;

  }

  _movieLayer.actions = [NSDictionary dictionaryWithObject:animation forKey:@"position"];

Now when ever the position of the layer is changed it will follow the path instead of the default linear interpolation. There is a lot more to be said here but this at least gives a quick intro do using the keyframe animation. Here is the code.

Have Fun!



Comments:

Hi Bill,

I was hoping I could pick your brains.

I'm implementing some animations in one of my apps and am having trouble getting the CABasicAnimation which the system uses when implicitly animating on a -setFrame: call. I need to get the animation object so I can set a delegate.

In the docs the keys are listed for CALayer, but not for NSView. As @"frame" does not seem to work when passed to -animationForKey method of the view's animator proxy.

Thanks,

Jon

Posted by Jonathan Dann on April 17, 2008 at 01:13 PM MDT #

Hi Dan,

Thanks for the comment.

The problem I think is that @"frame" is not actually the key that is being animated. Instead when you change the frame both the frameSize and frameOrigin are animated. Try replacing one or both of these animations and you should have better luck.

HTH

Posted by Bill Dudney on April 17, 2008 at 01:13 PM MDT #

Thanks Bill,

I'll let you know when I've had a second to try it.

Take care,

Jon

Posted by Jonathan Dann on April 17, 2008 at 03:25 PM MDT #

Hi Bill,

Excellent stuff. I have a quick question on keyframe animations: When the sample animation I created to test things out ends the object I am animating warps back to it's original position; is there a setting to have it remain where is after the animation concludes?

Posted by Keith on October 16, 2008 at 10:35 AM MDT #

Hi Keith,

Thanks for the kind words.

There are two approaches you could take, first set the removedOnCompletion attribute to NO.

Second you could set the value you are animating to the new value.

i.e.

myLayer.position = newPoint;

HTH

Posted by bill Dudney on October 16, 2008 at 10:35 AM MDT #

This is a nice example.

I actually found this post while Googling around, searching for a way to animate the fill or stroke of a path. I can't figure it out, do you have any pointers? I want to take a CGPathRef I have and fill and/or stroke it in an animated way, and CAKeyframeAnimation seems built for this, but I can't figure out which property should be animated.

Eric

Posted by Eric on January 17, 2009 at 04:39 AM MST #

Hi Eric,

The book has an example on how to do what you are asking about for NSViews. Its not super efficient but will work, and with only a slight mod should work with CALayers as well.

Posted by Bill Dudney on January 17, 2009 at 04:50 AM MST #

Hi Bill,
I'm developing an app with objects moving along paths, this objects are images with an arrow...and i need the arrow to point in the right direction (where the path is going)...i know i need to implement some roration animation too...can you give any pointers?

Posted by Azul on August 02, 2011 at 03:31 AM MDT #

Post a Comment:
  • HTML Syntax: Allowed