Record Your Core Animation Animation
Every once in a while I find a way to combine multiple technologies that, while they don’t produce anything terribly useful, are very interesting when combined. In this post I will be taking a look at combining Core Animation and QuickTime. As you may or may not be aware, you can draw in a graphics context while your Core Animation animation is running and add each image created to a QTMovie object from QTKit. This enables you to create a QuickTime movie of your Core Animation animation. Here’s how.
The basic process flow goes like this. Clicking the ‘Capture’ button on the user interface calls an IBAction called-saveAnimation. It prompts the user to select an output file for the movie. When the user has selected the file, the animations are created and added to the layer. Next we create a timer that is going to call a function that will grab the current frame and place it into the QTMovie object using -addImage at a specified interval. We set our AppDelegate to also be the delegate for the animation group so that when the animation completes we get notified and can then write our QTMovie object data to disk.
Use An Interesting Animation
First you are going to need an animation that is worth recording. Of course any old animation will do, but we’ll keep it interesting by adding multiple animations to a single layer. I have created four different keyframe animations that we will add to an animation group. The keypaths are “backgroundColor”, “borderWidth”, “position”, and “bounds”. Check the sample code to see how these animations are constructed.
We set the duration for all of the animations to five seconds. We also need to make sure that we set the duration for the group itself, otherwise it will override the five second duration we set for the animations themselves and run in the default 0.25 seconds. The code below shows how the animations are added to the layer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Notice that we have used KVC here to set a name for the animation group. We will use this as a tag later to make sure the animation that triggers our-animationDidStop:finished animation delegate is the correct one. More on that later.
Get Our Movie Ready
Prior to loading the animations, we prompt the user to select a file to write the movie file to. After loading the animations, we start our timer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Our -updateTime selector will get called every 1/10th of a second and will grab the current frame to save it to the QTMoive object. Here is the-updateTime code.
1 2 3 4 5 6 7 8 9 10 11 | |
Obtaining the Current Frame
The code to obtain the current frame is somewhat lengthy, but the concepts are pretty simple. We need to create a graphics context that we can draw into and then draw into it using the presentationLayer of the contenView’s root layer. If you’re not familiar, the presentationLayer provides the current state of the animated fields while “in-flight”.
Core Animation doesn’t provide any callbacks for when a frame is ready to be displayed which is why we are using a timer. This means that we may be capturing more frames than we need to, so getting the right frame rate takes a bit of trial and error, which I have to confess I wasn’t able to get nailed down completely. I’m still working on it and will update here when I get that part figured out. Meanwhile, here is the code for-getCurrentFrame
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | |
Notice that we are calling -renderInContext on the presentationLayer of the window’s contentView’s root layer. If we were only to render our animated layer, we wouldn’t be able to see the animation as it will only render the containing rectangle of the animating layer.
Finishing Up
Finally we need to write the movie data out to disk. The QTMovie object provides a single call to do so, but we need a way to know when the animation has finished so we can make this call. When we created our animation group, we set its delegate to our AppDelegate which will cause the delegate method -animationDidStop:finished to get called. Remember that setting the delegate for each of the individual animations gets ignored when you are using an animation group. We implement the-animationDidStop:finished delegate as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 | |
The first thing we do is check the animation tag we set when creating the animation group. This really isn’t necessary in this code example since there is only one animation that is going to use it, but this code shows you how to differentiate if you were to use multiple animations or groups and wanted to know when each of them finished animating.
The call to -updateMovieFile writes the data to disk and we now have a QuickTime movie that will play our animation. Open the resulting file in QuickTime or just invoke QuickLook to see the result.
Conclusion
Maybe you can think of a use for this kind of thing. I haven’t yet–other than for writing a blog post of course. Shoot me your thoughts and comments in the comments section. Until next time.
CA Animation Capture Demo Project
http://www.cimgf.com/2009/02/03/record-your-core-animation-animation/