Graphics, Media, and Games • iOS, OS X • 59:09
Core Animation is the layer-based animation system at the heart of the dynamic user experience seen in iOS and Mac OS X. See Core Animation in action and explore its intuitive programming model for creating compelling animations. Understand how to maintain high frame rates and learn recommended practices to deliver smooth transitions and effects in your apps.
Speakers: Michael Levy, Tim Oriol
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Well, welcome to this session of Core Animation Essentials. My name is Michael Levy. This is Tim Oriol. We'll be doing this presentation together. I've really enjoyed coming to these sessions. I've just been amazed by some of the stuff I've already seen that you guys have done. And I hope that the session we give you will help you punch it up even more or, if you're new, help you get those same incredible apps on both the Mac OS X platform and on iOS. And before we even get into this talk, I'm going to hand over to Tim for our first demo.
All right, so for the first demo, the whole point was just to give you a taste of what kind of things you can expect Core Animation to give you or add to the current things you can do with UIKit for your user interface. What we have here is an app that allows the user to select an item.
Here we have photos. And of course, we want to give good visual feedback to show which item is selected. So if we tap on one of the items, you can see that the photo is enlarged. And if you're only doing this, you can do it and you probably should be doing it with UIKit. You change the bounds and UIKit behind the scenes is going to use Core Animation to get this smooth animation. So if you go to another photo, the old one will animate down and the new one will animate larger.
If we move to effect number two, now when we tap a photo, it's going to pulsate, so it's going to shrink and grow. And this you could do with UIKit, but you're going to have to mess around with timers. And if you want to do this, we're going to talk about the CA media timing protocol later on, which you can leverage to create repeating animations to add to your layers for a specific repeat count or to repeat forever.
If we move to effect three, now this is something you really want to use Core Animation for. One of the great things about Core Animation is it gives you border width and border color properties for any layer that you have on the screen. And as with most Core Animation properties, these can all be animated. So we apply a repeating animation and we're going to make the border color light yellow, dark yellow, light yellow, dark yellow, and to sort of highlight which one is selected there. And now let's move to effect number four.
For 4, we get a card flip animation. The layer is going to twist around in 3D space and we're going to bring it up center and close to the user's eye. This is using what we like to call the 2.5D perspective transform that you can set up in Core Animation. It takes a little bit of work to get set up, but after that it's pretty intuitive to position your layers on the screen.
Let's move to effect five. Now when we tap on something, you can see we have this sparkly blue aura around the selected photo. This is something you can also do with Core Animation, and I'm going to talk a little bit more about that at the end of the talk today. Thank you. Back to Michael.
Okay, thank you, Tim. So what are we going to cover today? There's quite a lot to cover. We thought we would go from really from the beginning right through to some more advanced topics. So we've got a lot to cover. We're going to try and pack it up. So whatever your level of experience is, hopefully there'll be something for you in this session.
We'll start with fundamental concepts. We have three parts to the talk. At the very heart of it is layers and layer properties. We'll talk quite a bit about them. And then, of course, how you animate layer properties, including a section about what animation is. We will then cover topics in Core Animation, including layers, 3D transforms, and perspective, the presentation layer versus the model layer, notifications and timing, and a little bit about performance. And we'll end that section talking about the core animation. We'll talk about masks and shadows. There are, of course, other topics, and I urge you to look at previous presentations for more information.
We just didn't have time to get everything in. And we'll end with a little bit extra that's quite fun at the end of the presentation. So let's just start with the architectural overview so you get some sense of what core animation really is and where it fits into both the iOS and Mac OS X frameworks.
So, you know, it's common to use this kind of block diagram with UIKit or AppKit at the top and the graphics. And you can see that the graphics hardware at the bottom and the core animation is a framework, and it fits in between there. It uses core graphics.
It uses OpenGL, either ES or OpenGL, depending on the platform. And they, in turn, talk to the graphics hardware. But one of the great things about developing for the iOS and Mac OS X platforms is that your app can always reach down from the top level as far down as it needs to go. Now, hopefully, the higher up you are in this block diagram, the more that's taken care of for you. So you can get great effects very concisely just using UIKit.
If you want to then punch it up again, you can then drill down a little bit into core animation and so on. And the design philosophy of core animation is to take all those common and tricky tasks, such as, for example, doing the mathematical computation of interpolation points and making that all automatic for you. How to do transformations that do these great effects, like the flip effect that you saw on the card. The details of that are all taken care of for you inside the core animation framework.
I won't talk about this slide a lot, but just give you some sense of what's happening behind the scenes. It's what's called compositing. That is, you have a bunch of layers that the user will see, but before each frame gets rendered, there are a bunch of steps that have to go through. So, firstly, there's a kind of sorting that has to go on because layers typically go on top of each other. And as we'll see, you can affect that positioning.
You have to merge the layer background with the layer content and then apply borders and shadows and so on. And eventually, the visual thing that you want to show your user shows up on the screen. And these steps may have to occur for the production of every single frame. And especially when you're worried about getting good performance, it's helpful to have this picture in your mind while you do the design. But let's just step back a bit and go to the real basics of this. And that is just what is animation.
Now, I'm going to use here what I'm now calling the hello world of animation, which is we're going to have a layer. It looks like a ball, and we wanted to do something like drop to the bottom of the screen. So, in core animation, you achieve that in a very simple way. You just change the position property of the layer to a new position, P, and that animation occurs, and you get this nice, smooth animation. Now, behind the scenes, there are two important things that have to be computed by core animation.
The one is the points, the interpolated points, and the second is the timing. Now, by timing, I mean a kind of timing function that controls, how the animation appears to you. Is it very jerky, sort of infinite speed and then stop? Or does it start going faster, reach its maximum speed, slow down? Well, you can control that with timing function properties. So, those, and the defaults set for all of those.
So, normally speaking, you don't have to worry about it. You'll get good default behavior, which will make your app look great. But you can override it if you need to. And then, of course, once those things are known, then, of course, what is animation? Well, it's just a matter of core animation, producing all the intermediates. So, you can do that by taking the intermediate frames and rendering them onto the device, hopefully at a good frame rate, like 60 frames per second.
So how do we do all this? Well, let's start off with layers. They're really at the heart of it. And the layers in iOS come for free, as it were. Behind every UIView object is a layer. And you can reach it by using the layer property of the view. And if you're just doing a standard UIKit application and you have a draw rect method for providing content, then the draw rect is actually rendering behind the scenes into the layer that backs your UIView.
In Mac OS X, it's slightly different. There you have to tell the NSView explicitly that you want to use a backing layer and you have to tell it which layer you want to use. After that, everything works identically, both on Mac OS X and iOS, with some minor exceptions that I won't go into, but are well documented. So the CA layer hierarchy looks like this. I've left out some of the subclasses that exist. We'll talk about some of these subclasses. At the top is CA layer itself.
And that is not an abstract class. You can instantiate CA layers. And in fact, in many cases, that's all you ever need to instantiate. So in demo one, for example, all we did is make nine, was it nine, 12 instances of CA layer, populated them with content, and that was it. There was no need to write any explicit drawing code. And I'll talk about why that happens and the advantage of that in a couple of slides from now. There are a couple of interesting subclasses for specialized purposes, like CA shape layer.
There's a lot of other things that you can do with CA text layer and so on, and we'll talk about some of them during the presentation. So let's move on to the next demo. And the purpose of this demo, very simple-minded one, but just to start you thinking about CA layers and how you might use them in your applications. I used a playing card because I'm assuming that almost everyone on the planet has seen playing cards at one time or other, so I don't have to explain too much. So let's start with the first one. So let's start with the first one.
[Transcript missing]
The layer and sublayering model is very similar to UIView with the only exception being that it's a lot more lightweight. So it doesn't come with gesture recognizers, for example. But otherwise it has a whole bunch of very similar methods like the ability to add sublayers so you can have an arbitrarily nested tree structure of sublayers. You can insert sublayers in different positions. The default is to put them one on top of each other as the name layering would imply. And then there are methods like set needs layout which allows you to reshuffle your layers.
Layout set needs display if you want to force a refresh. Very often that's handled for you automatically. But on occasion you'll make a change that you have to explicitly notify needs to be refreshed. And if you subclass CA layer you can implement a draw in context method to provide explicit context using Core Graphics.
A more common way to do it though is to set the delegate property in which case the delegate object can if it wants to implement the draw layer in context method. And it can then provide content for one or more layers. The transform property is a very important one and the transform unlike Core Graphics is a three dimensional transform matrix. So there's three coordinates x, y, and z. And in the part two of the talk we'll talk about what we call the 2.5D model.
Um...
[Transcript missing]
We apply that, we set the transform property to be that composed transform, set the position, of course, near the bottom right, and there's the heart pip. And I leave as an exercise for you to figure out how you would do the others, pretty straightforward, once you get the concepts to do the rest of the card.
The layer geometry is very straightforward, important to understand. Luckily, there's not too much to it. The important concepts are obviously the bounds, which tell you the size, the height and width of the layer, the position, which is a point in the superlayer's coordinate space, the anchor point, which I'm going to talk a bit more about in a second, and of course, this three-dimensional transform property.
So, the anchor point, sorry, let me just go back to the slide. The anchor point is a point whose values, whose X and Y values range between 0 and 1. And in the coordinate space I'm showing above here, which is the iOS 1, the default is 0.5, 0.5, which says place this in the middle of the layer. If you use, for example, 0, 0, it would be in the top left-hand corner, the 0, X, 0, Y.
If you use 1, 1, it would be in the bottom right corner. And where you set the anchor point affects positioning in superlayers and some of the property changes. So, for example, if we rotate around the center with the default position, you'd get what you expect rotation around the center point.
But if the anchor point had been set to the lower left-hand corner, which you would do by setting it to a point 0, 1, then we get a different rotational effect. We rotate, in that case, with the same transform around the bottom left corner. So, if you use 1, 1, X, 0, Y, which says place this in the bottom left corner, the 0, X, 0, Y. of the screen, in this case a transform around the z-axis again.
So that's my very quick introduction to CA Layers. Let's get on to the neat stuff, the animation. How do you do that? So just to go back to basics a little bit, I've already showed that frame-by-frame thing. So if you stop and think about it a bit, you'll realize that animation is really the interpolation of a bunch of points starting at an initial point, going through some interpolated points, and ending at a destination. And we explicitly use the key names from value and to value. You'll see them crop up later on when we talk about explicit animations.
There are in fact two styles of animation in the Core Animation framework, implicit animations and explicit animations. And we'll start with implicit animations. Now supposing we have a picture here and we wish to just... fade it away, then we just do that. It's declarative, you do that, and the picture fades away. Now notice it didn't disappear, it faded. Maybe that was too quick for you to see because it used the default animation duration, which is one second.
There is a class called CA Transaction which lets you override those defaults. And I'll use two of them now because I'll do a slightly more complicated implicit animation. In fact, two implicit animations. I'm going to move the image to the bottom of the view and fade it out, but with different timing. I'm going to make the fade last longer than the move.
So, and I do that like this. Let's see if it works. So notice, I don't know if you could notice the opacity going away as it was moving down, but the idea is to move it in two seconds and fade it in five. And this is the code that I would use to achieve that. I chose this example to point out an important little gotcha, which is you can use CA Transaction, to set properties, and we'll talk about more of them, the media timing properties.
One that you'll use very often is animation duration, but you have to remember that the value that's in effect dynamically at the time that your property change is made is the one that will be used. So if you took the myLayer.position statement and put it one line above, then it would use the default one second value, not the two second value. And of course, the advantage of that is you can apply implicit animations using different CA transactions values.
So what sorts of things can be animated? I'll come back and talk about CA Transaction a bit more as we proceed. What are the sorts of things that can be animated? Now, I've used position a lot because, as I said, it's the kind of hollow world of programming. But how many of you have ever shipped in your application factorial? I'm sure none. But that's just the hollow world. Now, of course, you will be repositioning objects in your applications. But remember that many other sorts of things can be animated.
And I've just put together a composite of a few of these to give you an idea. So, for example, opacity, which we've seen. You can change size, position, which we've talked about. Nice one. There's a nice rotational flip. Shape is a fun one that you can animate and even color change.
And if you look at the documentation on a CA layer, you'll see listed next to the various properties the term animatable. And that's your clue that that particular property has interpolated points that can be computed for you. All the implicit animations that you apply are done together in the next invocation of the run loop.
So after your method's called, it goes back to the run loop. They're all applied as a single transaction. The CA transaction class will let you control not just duration but the timing function. That is the type of animation you want to do. And a few more which we'll get to in part two of the talk. And as I've said, the CA transaction properties are applied at the time. The implicit animation. The implicit animation is created. Sometimes you don't want an animation.
So for example, if you have a gesture recognizer that's -- and the user is moving an object around the screen, you don't want to lag while the animation animates. The point, you want that to be applied as fast as possible. So the way you do that is by calling the CA transaction class method called setDisableActions with the parameter yes.
And a little gotcha -- or it's got me anyway -- is there's also a method called disableActions. And that's not a verb. That will just tell you what the current value of that property is. So just remember you have to say setDisable to yes or no. Now, when you set that, it only applies for the next run loop. So if you want to have that sort of in effect all the time, you have to call it again when you do it again.
So to understand implicit animation, I thought I'd go inside the mind of Core Animation. Remember I showed that little blobby brain there. So we're looking inside this brain a little bit. What's happening behind the scene now? This is something that you're very unlikely to have to do in your applications, but it's helpful to understand what's happening when you do an implicit animation because it can help you solve other problems, which I hope to demonstrate in the next couple of slides. So there's a protocol called CA Action Protocol, and it deals with actions which are objects that can respond to events. And typically, a typical kind of action is CA Animation, which implements the action protocol. And the basic method of CA Action Protocol is to execute an action.
And if an action is found when you do an implicit property change, then it's added to that layer behind the scenes using add animation for key. So let's go through this a bit. So imagine you have a layer and some property gets set. Well, what the setter code, the default implementation of the setter code will do is send action for key to a delegate if that layer has a delegate. And that may or may not return an action.
If there's no delegate or it returns nil, then the setter will then look up in the action dictionary, which is a property of CA layer. And again, that may return nil. A couple more steps and finally it'll call the CA layer's default action for key. And these are, of course, implemented in the framework for all the animatable properties. And when that action is returned, it's then added to the layer.
as an animation where the key that's used is a key value that corresponds to the particular property that you've changed. And we'll come back to this slide in a few seconds. You'll see how useful it is to understand that little sequence of events. But I just want to reiterate that in your code, you're unlikely to have to worry about the CA Action Protocol. Okay, so let's move on now to explicit animation. It's a more complex class hierarchy here. In this case, CA Animation is an abstract class. In practice, you instantiate one of the three leaves here. The CA Animation Group, CA Basic Animation, or CA Keyframe Animation.
And I'm going to go back to my Hello World example to illustrate how you do this in practice. Now, I just want to point out again that I've already showed you how to drop a ball using implicit animation, and that was one line of code. It's maybe two if you want to change the duration. So, you know, generally, why write five lines of code when you can write one line of code? So, in practice, you probably wouldn't do this particular action with a basic animation.
But pedagogically, that is, when I try and explain it, it's a nice example because it's easy to understand. So, that's the effect that I want to achieve. Not implicitly, this time explicitly. So, what I do is make an instance of the CA Basic Animation class, and I set the position.y as my key path. So, that's a key path that corresponds to the y coordinate of the position property. of the layer to be animated.
Of course, you can apply the same animation to several layers. And remember I said that an animation has a from value, a to value, and interpolated points. Now with basic animation, all you do is set the from value and the to value, and the framework will work out the interpolated points. And now instead of using CA transaction for the animation duration, we explicitly say that we want the duration of this animation to be five seconds.
We add that drop animation to the layer and the key property I don't need to fill in, so I'll set that to nil. And we'll see how we can make different use of that later on. Now, so that example will achieve what you want, except that in reality, if you took exactly that bit of code and stuck it into your project, what you'd actually see is that. And not quite probably what you expected. Now, why did that happen? Well, it happened because, and this is important. Explicit animations do not affect the model values of your layer hierarchy. They create animations, but they leave your model unchanged.
And I'll come back to this point a few times. Now, this is great because it means you can do all sorts of flying things, it was around the screen and move and follow gestures and so on. And at the end of the day, you haven't changed anything in your model, which is most often what you want.
But it can be irritating when you hit this little effect and say, what the heck's going on? So we want to change the model value. So we do that. We do that by setting the position to a new value at the bottom of the screen as highlighted there. And then as before, we create the drop animation.
But we now are armed with the knowledge from a couple of slides back of what's happening behind the scenes. And if you remember what I said is that when you set a property implicitly, there's a little bit of work to go and retrieve an action and then add it to the layer. And furthermore, it's added using a key that corresponds to the property that you've changed. In this case, position.
So. By using the key parameter. And by using the key parameter, we can now change the animation. And in the last call, we effectively cancel the animation that was scheduled by the implicit model change. And doing those two steps together will mean that the ball will drop and the model will be changed.
Now, if you don't want just these default interpolated points, a linear interpolation from start to finish, then you use keyframe animations. And so here the picture is a bit more complicated. So the keyframes are a bunch of points that you can set in two ways that I'll explain in a second. And you leave it up to the framework to compute the interpolated points using basically three different techniques.
So how do you set up keyframe animations? Well, a keyframe animation class uses either a path or a value of path. There's a CG path. And if you use the path property, then the control points of the path will be used as the keyframes. Or you can explicitly set a whole bunch of values. And there's an optional flag, key times, which lets you override the default timing. The default timing, I think, is linear. So this will let you change the timing.
So you can have a long delay before going to the first keyframe and then very short to the next one. And these are numbers that go from zero to one fraction of the total time for each keyframe segment. And the interpolation is either long values or the path. And as I said, three different interpolations. Linear, which is what you think, a straight line interpretation. Discrete means no interpolation. Just go from keyframe to keyframe. And cubic fits a nice, busier path to the keyframes.
And then finally, the last part of that class hierarchy was group animations, which is a collection of animations applied simultaneously. And the timing is clipped to the group timing. So however long the animations within the group are, the overall animation will just last as long as the duration property of the group. Very easy to do. You create a whole bunch of animations. You then instantiate a group animation.
And you set its animations properties to be an array of your animations. And then when you add them, you set some duration for the group. When you add that to the class, all the animations will be applied at the same time in the group. Tim, so we have a demo that try to put all these concepts together for you.
Thanks, Tim. I just want to apologize for implanting that little sound meme, because you'll be humming that for the rest of the day, and my apologies for that. But we chose that example. It's somewhat bogus and made up, but we chose it because what I want to do is just walk through some of the code snippets that we used to put that together, just to see how to put these three types of explicit animations together.
So, now, I made the song using GarageBand. I used some synthesizers and some MIDI, so pretty straightforward. And the nice thing about using GarageBand is I could set the tempo explicitly, so I set it to 150 quarter notes per minute. It's in 4-4 time. I'm not much of a musician, but I knew enough to be able to do that. And so the bouncing ball itself, we just did with a position animation.
And, as I have to confess, now, you could compute these, and that would be the right way to do it. For the purpose of this demo, we just, you know, touch points and just recorded them and then stored that all in an array, so a bit of a cheat to do that.
In general, if you have arbitrary music, of course, you have to do some font metrics and figure out where all the beats are and so on. It's a bit more complicated. And then we set the duration to be the number of notes divided by the tempo, convert that into second, because the tempo's in minutes.
And, oh, yeah, this is lying a bit. We started with cubic spline, but you couldn't really see the bounce points very well because it was rounding all the corners. So in the demo, I actually changed it to linear interpolation, so you get a more pointy appearance, and it's more clear when the ball hits the words.
And then for the opacity animation, this was a bit more complicated. In this case, what we want to do is when the ball, I don't know if you saw this, but when the ball got to the end of the line, it sort of faded away and then moved to the left and then reappeared.
So we had to set an animation that went from one to zero and back again. And so we did that with an opacity animation. And there's a little bit more complication to work out. We knew that each line had, I don't know, four beats, two measures, I think. Anyway, all the calculation done. And once that's done, we used, in this case, used the values array for when the opacity change would occur, but the key times to control the timing of that.
And then finally, just to illustrate that, I stick them in a group. Now, we didn't have to do this. We created a group. We set the animations of the groups to be bounce and opacity. We set the duration to be the same as the bounce duration because it used the tempo and the number of notes to compute the length of the whole song.
And then added it. This is really more of a convenience. We could have explicitly just added those two animations, but sometimes it's handy to have them all in a group. So, for example, if you want to cancel all your animations and you have them in a group, it's very easy to do that.
Okay, so that's what happened with that demo. So let me just summarize this. This is the end of part one, if you like, introduction to Core Animation. Let me just summarize what we've covered. So we use CA layers for content with a declarative style. That means that when you change layer property, a whole bunch of work will happen in the background for you, almost always giving you exactly the beautiful effect that you want. If you want to start modifying the default properties, you can using the CA transaction class. If you want to get even more fancy, then you use explicit animations of which there are three types: CA Basic Animation, CA Keyframe Animation, and CA Group Animation.
All right, so now talk number two. Now what I'm going to do here, it may feel a little bit spotty, I apologize for that, but we try to pick topics that we think will really give you a good sort of overview of the more advanced stuff that you might have to deal with. There's more and I'd love to have been able to cover it, but it's just impossible in the hour that we had to do this.
So we try to pick topics that we thought were the essential topics, and the first one of which will be shown in the demo, which is Perspective Transform. And now Tim's going to do a whole bunch of things, and I'll just sort of give some narrative as he do this.
So here we have five layers with background colors and some numbers in them, and we move closer to them. But now notice what happens. Now when we rotate around, we can see that there's perspective. Perspective has been applied. So the number one, for example, is tapering off to infinity, and number two is expanding out towards you, so it gives the user a sense of depth.
Now, in fact, I said there were five layers. There's actually a sixth layer which we can expose, and we do that by changing something I'll talk about a bit called the Z position. And to show you the sorts of things you can achieve, I assume that's what Tim's about to do, we just close this little box. Now, it turned out to be relatively simple to achieve that.
The key was making sure the anchor points of the layers that were going to take part in closing the box, you know, the back one just sits there, but the ones on the sides and the ones on the top move up and down. And the key idea there was to put the anchor points on the axes of rotation that we were going to use and then do a rotation around those axes. And that really was all. The lid, number five, that was simple. That was just a matter of moving it into the right Z position. All these layers are identical size.
And we can open the box. So again, just the anchor point on number five and do that rotation by pi by two. But the nice thing about this is you can see that you're getting another important part of three-dimensional effect, which is that the bits of the layers that should be occluded from vision are. So you can only see part of number six unless Tim happened to rotate around.
And there you can see all of six. So now this is all achieved. Why is it called a two and a half D? It's called that because this is all achieved just using these a Z position, which we'll get into plus a certain perspective transform. But the objects themselves, the layer CIL objects are not full 3D objects. So unlike OpenGL. So that's why it's called a two and a half D model. Do you have more to show us? Okay. So putting the back box back in order.
Okay, so let me just go into this a little bit more detail. So let's suppose we're going to add three layers. So I'll add a blue layer, a green layer, and a gray layer. Now, if I change these, so there's this Z position, which we've mentioned the default value for the Z position is zero. And if you're doing a just straightforward 2D application of Core Animation, you won't ever worry about the Z position. We can change it. So here I've set it to 500. Remember, the Z axis comes directly towards the viewer.
And therefore, that layer, which was in between the blue and the gray, pops to the front. But notice that it didn't change its size. And that's because in that demo, I have not yet applied any perspective transform. So merely changing the Z position will not give you any perspective.
How you get the perspective is by using a property called the sublayer transform property. And you apply it to the parent layer. So all the sublayers of that parent, which has a sublayer transform property, will be applied to the parent layer. And then the sublayer transform property set will have an appropriate perspective transform applied to them. The type of transform used is called a homogeneous perspective transform. And obviously, I'm not going to give you a math lesson right now, but if you enter that as a search term in any search engine, you'll get lots of good references on that. It's very commonly used.
All you have to do as an app developer is really this next line over here, which is you want to set the distance of the viewer's eye or the camera, if you like, at some distance from the origin of the Z coordinate along the Z axis. And you do it with that statement.
So the thing IZ that you see there, that's just a constant that you've set somewhere. So it's just a floating point number. And it represents the distance of the viewer's eye from the origin of the Z. So if you set it at zero, your face would be right on the device like that.
And as it increases, the device, you know, the apparent view of the object gets further and further away. And of course, that transform, can be composed with other transforms so you can get the sorts of rotational effects that you're looking at simply by doing a composition. In this case, just doing a straightforward perspective.
And now let's see what effect that has on the same example, except that we'll now use the Z position, assuming that that sublayer transform's already been set. So firstly, I'll add the blue sublayer. It looks like nothing's happened. You can't tell yet. But notice when I add the green layer where I haven't explicitly set the Z position, it looks bigger.
And that's because this perspective's been applied. And similarly, if I set the Z position of the gray layer to 100, it will appear to be in front. And now it starts to, you can see why this is starting to look like a sort of cover flow or Time Machine has this, exactly this kind of perspective transform. Of course, they're also, the viewing perspective is changed a little bit because you're slightly to the left. Otherwise, you just see the gray layer. So that's why I say you can compose these transforms. poems.
What happens if layers overlap? Well, it's supported, but there are some gotchas. It's a lot of extra work for the renderer. They're rendered more than once. Now, it works by using a depth sorting and using the layer bounds and position to determine what's occluded and what's not. And if some of your sublayers don't fit within the bounds of their parents, it won't always get the rendering right. So use with care. As I said, it's not a full 3D model. It's a 2.5D model, but I hope the cube example that you're seeing here is a 2.5D model. At least it's convinced you that.
Nevertheless, there's lots you can do. Okay, another topic, which is an interesting one, notifications and timing. So how long do animations last? How do you get a whole bunch of animations to work in coordination with each other? What happens when they stop and so on? So I'm just going to cover those in a couple of slides.
And they all use the CA Media Timing Protocol, which is adopted both by CA Animation and CA Layer. And the typical properties, I'll talk about begin time in a second, repeat count, repeat duration. Now, you use one or the other. If you use overall duration and repeat duration, then it'll figure out how many repeats there are. But the idea of repeat count is, for example, with the bouncing ball example, the moving the ball, if I added a repeat count of 10, then it would go up and down 10 times.
There's a duration, which is how long it lasts, and an auto-reverse, which is handy if you're doing some complex keyframe animation, or a flip transform, for example, because it'll reverse along exactly the same path that got it there in the first place. And finally, fill mode, which I will explain when I go through another example a bit later on. Now, in terms of notifications, there are two mechanisms you use. If you've created explicit animations and the animation has a... You've set the delegate of the animation, then the delegate can implement animation did start and animation did stop.
And they'll do exactly what you think. When the animation starts, the first one will be called. And when animation did stop, the second one will get called. The finished flag will tell you if the animation came to a safe, normal end of life or was somehow violently disrupted for completing because, for example, it was removed or canceled for some reason. So you can use that flag for that purpose.
If you're using implicit animations, you can use completion block, which were introduced in iOS 4. Of course, I think they've been on OS X a bit longer than that. So here's an example where I'm doing two implicit animations, setting the opacity to zero and the position, and I'm setting a completion block, and in this case, it's disabling actions and removing the layers, doing some cleanup work. Now, remember the important point.
You must call that before you change the implicit properties because otherwise the default block handler will be called, and the default block handler does nothing. So remember to do that code first. Now, I've just got a general principle. It's tempting, and in fact, when I started with this, I thought, oh, animation did stop. That's a very nice way to chain things together. In fact, it's not the best way to do it. And in fact, best use of notification is probably for setup and teardown if you need to do them. For timing, you can get much better control using the CA.
It's called the Media Timing Protocol, and I want to illustrate that with this example. So I have three balloons in my little Carnival application, and what I want to do is I want the balloons to go up and down one by one. So this is the effect that I want to achieve. So as the balloon drops, the next one goes up, and I could have many of these. Now, I could do this by saying animation did stop, but the trouble with that is it happens during the run loop.
It's not necessarily going to happen immediately. There may be other code going on, and it's also architecturally in my opinion. And I think it's a very nice way to do things. So here's a much nicer way to do it. Now, there's a bit more to understand, so this is also a good time to introduce that.
So let's start with time. Now, it turns out that every layer and every animation also has a time coordinate. So it's really a four-dimensional space, if you like. So the first thing you have to do is make sure that your layer is running in local time. And there's the statement that does the conversion. I use a helper function called CA current media time. To give me the, if you like, the machine's clock time.
I convert it using nil, say globally convert it to this layer time. Now, I won't go into why there are these different time coordinates. But just to say that you can, part of your layer hierarchy can have different time parameters. So for example, a subtree could be running twice as fast as everything else. And you can get some very nice effects doing that.
But I don't have time to go into all of that. However, it is important to get the right time zone, especially if you don't know for sure what's happening above your layer. In the layer hierarchy. And then what I did with these balloons is I'm going to animate the position using a basic animation. I'll make it auto-reverse.
I'll do it for five seconds. And then I use the begin time property, which I just glossed over a couple of slides back. And I'll set it to local media time, i.e. now, plus k, where k starts at zero but increments by five seconds. For every occurrence of a balloon in my array of balloons.
Okay, on that, we'll achieve that effect. Now, there's one gotcha with begin time. The default value is zero, but that doesn't mean that time starts at zero. It means that the begin time will be ignored if you set that to zero. If you set it to anything else, then begin time really reflects the local time of that layer, which may be machine time.
Here's another example which has come up in practice. Say you've got a scroller in your application and while scrolling is happening you have a heads-up display. And when scrolling finally ends, you want to fade that HUD away, but you want to do it after, say, a two-second delay. So, sort of a real-world example. So, firstly, we set the opacity of the HUD to zero because we do want the model to be changed after this is completed.
I do the time conversion, which I've already discussed, and our animation will call fade out. Its from value is 0.5, say, and its to value is zero. We want to make the fade last five seconds. And here's the key thing is we'll set the begin time to now plus two.
So, this animation begins after two seconds. Now, there's a slight snag, though, that's solved by the next statement. It gives me a good opportunity to explain fill mode. And the snag is this. If I didn't have this next statement, and I just ran that code, then there would be a two-second delay before the animation kicked in. Well, during that two seconds, the HUD model has an opacity value which is zero.
So, when that's being rendered by the GPU, it says, "What's the opacity?" "Oh, it's zero. Okay, go away, HUD." So, the visual effect of that will be that the HUD will disappear, and then two seconds later it will come back and start fading away. Not an effect that you want.
So, that's solved by using the fill mode property. And you can just think of fill as a function. It's expanding your animation at each end so that the from value or the to value can be dragged out to fill the complete duration of your overall animation. So, in this case, I'm just filling backwards because once I'm done, I don't care anymore because I've changed the model value. But there's also fill mode forwards and fill mode both and fill mode none, giving you, obviously, coverage in all cases. And then I just add the opacity animation.
[Transcript missing]
There's a performance, there's an instruments tool you can use called Core Animation and I'm going to skip this example because I want to get on to Tim. So, let's just move on to this one. So, what we're seeing here is instruments running on an actual device using the Core Animation plug-in for instruments and I've highlighted a field called Color Blended Layer and when I do that check mark, the instruments will set coloring where the red is bad. So, red is showing me that I am coloring the instrument. So, I'm doing some color blending. In other words, I have a non-opaque field. Now, this one's strange as a developer because in fact the two layers were almost identical.
They had the same content, they had borders and yet one is showing up with a red flag. And if you look very closely, you might notice the difference between two of them. I don't know if you can see that, but the difference is there's a corner radius. And even though I set the layer property opaque to be yes in both cases, obviously as soon as you set a corner radius, you want whatever's behind that, the outside part of the corner to show through. And instruments is telling me that.
And then as a developer, I can make the decision of whether it's really worth it depending on where the performance is being ruined or not to use corners. And that's a judgment call I can make. Now, I had an extra slide on masks and shadows. I just don't have the time to go through them, so a little unorthodox, but let me whip through these just to show you both. Shadows and masks and hopefully later you can review the slides and get a little bit out of this.
And the reason I'm doing this is because what I really want to do is hand over to Tim now to show you some very, very cool demos that he -- and I have prepared bitmap caching as well. Sad, but we'll move in. So, Tim, I'm handing over to you. Thank you.
Thank you, Michael. I want to talk about two of the more advanced subclasses of CA Layer. Before we said most of the time you're going to use CA Layer. These ones won't be used nearly as much, but they offer a unique set of features and powerful stuff you can do for your app.
So the first one I want to talk about is replicators. We have a CA Replicator Layer which offers the unique feature that it will automatically create copies of any sublayer that you add to it. And this is the full hierarchy. So any sublayer you add to Replicator Layer, it will automatically make copies of it.
So how do I control how many copies it's going to make? You want to look at the property instance count. Instance count is inclusive the total number of copies of each sublayer. So if I set instance count to one, it's not going to make any copies. So if I set it to three right now, I'm going to get two additional copies of any sublayer that I would add to it.
So I'm going to take this circle layer and I'll add that as a child of the Replicator Layer. And so what the Replicator Layer is going to go off and do is it will go and make a total of three of these layers. Now if you do exactly what I just showed you here, you'll see absolutely no difference in your app.
Because by default, all these layers are the exact same shape, the exact same size, and they're placed right on top of each other. So the whole idea is to change them a little bit. So the Replicator Layer offers a number of offsets that you can have applied to each of the copies. The one that I'm going to do first is a transform.
And so I'm going to create a transform that basically just shifts the Y position down by 50 pixels for each copy. So what's going to happen is the first -- the original layer will remain at its position, and then the second one will be 50 pixels down. And then the third one will be an additional 50 pixels down. So it's cumulative.
There's a number of other offsets that you can set. You can take a look at the headers for all the options. I'm going to show the color offsets as well. There's a green, red, and blue. Here I have a layer that's mostly blue and green, and so I'm going to set a green offset of minus 0.5. So we're going to knock out 0.5 green for the first copy that was made, and then for the final copy that was made, all of the green is gone, so it's a pure blue layer.
Where can you use these in their apps? How are they useful? A really good use for replicators is for people that want to do reflections. So you can see we can have a replicator layer that has an instance count of two, so we're going to make one copy, and then you can have that shifted down and faded out, and we'll flip it in the Y. So that way, if you're playing movies in your layer, if you add or remove other sublayers, it'll dynamically update your reflection, so you get this really nice reflection once you set it up.
It's kind of for free. The second one I want to talk about is particles. Particles you can use to create complex effects like water, smoke, fire, stuff like that. And we introduced these on the desktop before, but now we're bringing them to iOS. With iOS 5, you can now use particles.
I'm going to go over the basic construction of how you'd set up particles in your app. The layer you want to use is the CA Emitter layer. This is the origin of your particles. This controls where they are going to be generated from. So the properties you want to look at are emitter position, mode, and shape. So the shape, we have a number of supported options such as line, rectangle, sphere, cube.
So you pick one of those shapes and then the emitter position is where that shape will be located. So that will be the middle of that shape. And for emitter mode, specifies where on or within that shape we're going to generate the particles at. So we have an outline mode that if you have a rectangle, it's going to be only generated on the outline of the rectangle. Or there's ones like area, so we'll have it generated anywhere inside the rectangle. Or for 3D shapes like spheres, we can do volume, generate anywhere within the volume of the sphere.
Those specify where the particles are going to be generated at. And then there's the emitter cells property, which is an array of CA emitter cells, which you'll use to define each of the types of particles, how they'll look, how they'll behave. And so for each of these emitter cells, we have properties like velocity, birth rate, spin, scale. There's way more than this you can take a look at.
But basically we want to tweak these to create the unique effect that you're going for. The key ones, birth rate, is how many particles per second are going to be generated. And contents you can set to a CG image ref. Usually you want to use a grayscale image because that allows you to take advantage of the color offsets, the color properties to animate the different colors of the particles.
And in addition to that, each one of these CAE emitter cells has an emitter cells property itself. So what this means is that we can have particles that emit particles that emit particles for however complex effects you want to go for. And each of these are their own unique particle. You can set different properties on all of them. And you can use this to create really complex effects. And I'd like to show you a couple of those.
The first one I've got set up on here is a flame demo. We've got fire running here. And we have a particle that's being drawn with not full opacity, so we have this sort of buildup of opacity as we have particles go on top of each other. And I've set it up so that if we increase the slider, we're going to be animating the properties of these layers. So we're going to animate the birth rate, so we're going to get more particles per second coming out.
I'm going to change the emitter shape so it's going to be generated over a wider area. At the bottom to create a larger flame. And obviously the lifetime is going to be longer, so we get a higher flame. And as we start to increase it, you'll notice a second type of particle that I also have added for the smoke.
And this is a larger particle. And you can see the spin we've added. So it sort of looks like it's sort of twisting around as it goes up. So we have two types of particles here being emitted from one emitter layer. I want to move to the second demo now. We'll go out with a bang.
This is a great example of the nested particles. So we have a single emitter at the bottom here and it's emitting the first type of particle is actually invisible. There's no contents for it. It has no color. And this just follows the arc of the firework going up. And the reason you can tell it's there is because it emits three types of particles from itself.
So the first one is that flare off the back. And the second type of particles is the actual explosion at the end. And so we want a lot of particles in a really short amount of time. So we've used the CA media timing protocol to make the duration of that layer only .1 seconds.
And then we're going to use the begin time to make it only occur after a certain amount of time. So after it's reached the appropriate height, then we're going to have like a tenth of a second of generating 20,000 particles a second. And we get a huge explosion there. And then they'll fade out.
And then if you notice, there's a final type of particle at the end. There's sort of a little flicker after the explosion finishes. And I talked earlier about the different types of shapes that you could use to emit these particles. And I mentioned that they're not all just 2D. We can do all this in 3D as well. So if you touch and drag on the screen, you can see that we're doing this all in 3D.
And so I'm going to hand off this to you guys. You can do this on all of your new iPad apps, and I'm sure you'll have a lot of fun with it. Thank you. MICHAEL LEWIS: OK, so that is it. The related session is the one that some of you have just seen on UIKit, which you can catch later when you review the slides. More information, there are the contacts for you. And I want to thank you for your lively participation. We both had a lot of fun doing this for you.