Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2008-401
$eventId
ID of event: wwdc2008
$eventContentId
ID of session without event part: 401
$eventShortId
Shortened ID of event: wwdc08
$year
Year of session: 2008
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2008] [Session 401] Leveraging ...

WWDC08 • Session 401

Leveraging Cocoa's Layer-Backed Views

Essentials • 1:08:16

Take your user interfaces to the next level. Leopard provides the ability to render and animate Application Kit views using Core Animation layers. Learn how to build on this technology and take advantage of animation capabilities that will make your application shine.

Speaker: Troy Stephens

Unlisted on Apple Developer site

Downloads from Apple

SD Video (831.7 MB)

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Hello, thank you all for coming. Welcome to session 401, Leveraging Cocoa's Layer-Backed Views. Our topic for this morning's talk is an AppKit level technology that we introduced in Leopard, and we like to call it Layer-Backed Views. And I'll talk a little bit in a moment about what exactly I mean when I use that term. My name is Troy Stephens. I'm an AppKit engineer.

For the most part, this is oriented as sort of an intermediate-level talk, such that you may get the most benefit out of it if you've already had the opportunity to sort of get your hands in there and start experimenting with using Layer-Backed View functionality in AppKit. However, for the benefit of those who have not, and also to help put the rest of the talk in context, I will begin with a brief review, sort of looking at what Layer-Backed View mode is, and why we introduced this new operating mode, and why it might be interesting for your applications to work with.

From there, we will dive in quickly to new material and sort of the meat of the talk, which will be a series of tips and tricks-style topics designed to help sort of demystify the operation of Layer-Backed View mode and provide answers to some common questions that we get from developers about using Layer-Backed Views.

And I'd also like to point out that there are a couple of pieces of sample code that will be available very shortly that illustrate some of the techniques that I'll be talking about in this session, so I encourage you to download that sample code when it becomes available and examine it at your leisure. To get some more concrete ideas and code that you can copy and paste into your own apps or emulate to perform these techniques. So first, a quick review, sort of a recap.

What exactly is Layer-Backed View mode, and what do we mean when we talk about this? Layer-backed View Mode is a special operating mode of AppKit's NSView system of the view hierarchy that is based on Core Animation's rendering and compositing model and animation engine. Core Animation, as is widely known by now, is a very powerful framework for compositing and animating content. It is the basis for the amazing iPhone and iPod Touch user interfaces. It's an Objective-C API, part of the same Quartz Core framework that encompasses Core Image and Core Video.

Core Animation presents to you, the developer, the model of a tree or hierarchy of layers that are in many ways analogous to AppKit's NSViews and AppKit's NSView hierarchy. Each layer has its own position and orientation and bounds. It has content that it either displays or is drawn within it. Each layer has its own local coordinate system that it draws into, so it doesn't have to worry about what context it's in. Every layer has its own little world. Layers can have sublayers, and those sublayers can have sublayers of their own.

The Core Animation layer provides the tree or hierarchy construct, so you have the ability to build very complex kinds of scenes with layers. There are a lot of architectural similarities that you'll notice as you look at the CA Layer API and compare it with the NSView API, but there are also some very important key differences, and I'd like to talk about some of those today. One of the most important features of layers that makes Core Animation layer compositing so powerful is that you have per-layer content buffering. So this is distinct from what you're used to with working with NSViews with the ordinary compositing model for views.

You can see that you have a window where all of the rendering that views do becomes inextricably intertwined in the window. We're rendering into a single-window backing store, so any time you need to update some part of the window, you end up having to go through and redraw all of the views that touch that part of the window because all the data gets mixed together in the pixels of the window backing store. So when you're in layer-backed mode or when you're using Core Animation layers to render, you have per-layer content buffering. Each layer gets its own backing store, and obviously this costs a bit more in terms of graphic.

So you can see that you have a lot of pixels now, and you have a backing store for each layer. But at the same time, it enables some very powerful fluid kind of animation techniques because rather than spending a whole lot of time redrawing content that is static, we can very quickly move things around and just recomposite them as we go.

Along with this, Core Animation provides a very powerful implied animation model wherein, by default, changing any property of a layer initiates an animation or interpolation to the new value over time rather than immediately jumping to the new value. This animation model is also asynchronous in that Core Animation executes animations on a background thread such that they're not waiting on your main thread's run loop or things like that. It's all happening asynchronously in a way that everything is very nice and fluid.

In addition to this, Core Animation provides a very powerful and expressive set of animation description classes. There are six classes, four of them are concrete, and from those you can compose just about any kind of animation description that you can imagine, put together a request for a kind of animation that you want to show on screen.

And these classes are so powerful, CA Animation and all of its descendant classes, that we've adopted the use of these and lifted them up from Core Animation up to the AppKit layer. We're using them in AppKit APIs as the means for expressing animations for views and for windows. Thank you.

Core Animation features: Core Image Filter Effects, Shadows, and Masking. Core Animation also serves as a powerful unifying graphics architecture, the likes of which we haven't had before. Core Animation features: Quartz Composer, OpenGL, QuickTime Movie Playback, and Core Animation Layer Tree. There are a number of things that Core Animation does not attempt to provide that are beyond the scope of its design. First and foremost, Core Animation is not a user interface toolkit.

Although it is the foundation for other powerful user interfaces, such as UIKit on the phone and the Layer-Backed View mode of AppKit's NSView system on the Mac. You won't find things like buttons, sliders, table views, all the sorts of UI elements that you usually build your interfaces from, that you use to present data to users and allow them to edit that data. All of that stuff is beyond the scope of Core Animation, which is really focused as a graphics-level compositing and animation framework that's very lean and mean.

So in hand with that, there are a bunch of other consequences. You won't see things like high-level event handling mechanisms, the concept of input focus or first responder, the responder chain for delegating responsibility for handling events, things like accessibility support, drag and drop, tooltips, cursor wrecks, tracking wrecks. These are all user interface toolkit-level features that you really need something more like AppKit or UIKit to provide.

So we have these two sorts of frameworks, and when Core Animation came along, we thought, well, we wanted to really enable developers to readily take advantage of all these great animation capabilities to take advantage of the benefits of per-layer or per-view content buffering very easily. But we didn't want you guys to have to rewrite your user interface code or design and build whole new UIs in Interface Builder.

Leo Parmigiano-Rossi So basically what we came up with was the idea of offering automatic mirroring of a view tree into a corresponding layer tree, where you continue to work primarily with views and let AppKit automatically manage a layer tree into which that view tree is mirrored. And this basic concept is what I'm referring to as layer-backed views.

And to get a concrete visual illustration of just exactly what I'm talking about, let's take a trip back down memory lane and go to demo and take a look at Cocoa's Layer-Backed Views. So we have Cocoa Slides, which is the application that was used to demo this functionality when it was first introduced, and we'll launch Cocoa Slides.

This is a simple sort of image browser that presents a bunch of thumbnails of image files as slides for anyone here who knows what slides are or remembers. The main take-home point here, and I want to point out that this is available publicly as sample code, developer.apple.com slash sample code slash Cocoa Slides or search for Cocoa Slides on the ADC site.

This remains an excellent example of how to do a number of things with Layer-Backed Views. The interesting thing to note here is that this entire user interface is built using views. When you look at the source code, you'll notice there's very little in here that has to do with either animation or even less to do with layers directly.

All the code manipulates views directly, so these are all views in here, except this whole area, the sort of dark gray area, canvas area here where all the slides are housed, is Layer-Backed. So we can do things like very readily sort of move things around very fluidly. Because all these views have their content automatically buffered on a per-view basis. So we're not doing re-rendering, we're just moving things around, and Core Animation is doing all this on a background thread.

Leo Parmigiano, CEO, At the same time, these views remain fully interactive. These are standard checkboxes, NSButtons, in other words, the checkbox style of NSButton at the top of each slide. We can check off some slides. Let's say we want to show a slideshow. Here's another feature that we get by being layer-backed.

We have the opportunity to use all kinds of Core Image transition filters to very easily transition between one image and another. When you look at the code for this, it's really very simple what's being done. We specify a transition that we want to use, and then we're just pulling one subview out of the window, one image view that's showing the previous image, and stick in another image view. And when we do that, replace subview with operation through the containing views animator, the transition gets picked up and used, and it's really very automatic.

So you can do all kinds of things very powerfully like this. But the thing to remember here is these are all standard views. We can add things like shadow effects. That might be a little difficult to see, but it gives you a bit more of a 3D effect. That's something we just switch on. We tell the view, set shadow. Here's a shadow to use, and it all just sort of works.

So this is sort of, visually, this is what Layer-Backed Views are all about. Enabling you to work with views but leverage Core Animation to get some powerful effects and fluid animations. So if we could go back to slides, please. We'll talk about how all this is done. So how does this work in this application? How do you get your view subtree to become layer-backed? Well, we added a simple new interface.

SetWantsLayer is the setter method for a new wants layer property that views have. This was added in Leopard. You setWantsLayer yes for a view and it has a number of consequences. When you switch this on, AppKit automatically mirrors that view subtree. Everything from that view on down through its descendants all the way to the leaves into a corresponding layer tree. So each view gets a corresponding layer.

We call it the views backing layer because that's the backing store for the view. The views, as far as they're concerned, are drawing as they normally would. They implement draw rect and they get their draw rect called by AppKit. Only instead of the drawing they do going into the window, AppKit redirects that drawing into the views corresponding backing layer. So we're capturing content separately on a sort of per view basis, the way a professional animator might drawing cartoons on animation cells so that you don't have to keep redrawing things. You can just move things around.

To get animation effects more efficiently. Similarly, when you mark part of the view as needing display, we make that carry over to the layer. So we redisplay that portion of the view into the corresponding portion of the layer. Any view property changes you make, AppKit automatically maps those to layer property changes. We figure out which layer property needs to change so that the layer tree that the user is actually seeing in the window looks exactly like the view tree that you think they're seeing.

And this is what we call sort of a push model where changes are made on views. And AppKit pushes those changes over to the corresponding layer properties. So we expect you to mostly continue interacting with views wherever you can and let AppKit automatically push the changes to the layer tree and manage it.

Wherever possible, AppKit delegates animation execution to Core Animation's background threads that can be asynchronous. So if we can map animations that you request onto animations of layer properties, we'll do that. In some cases, that's not possible. For example, when resizing views, currently there's no general way for a view to tell AppKit how its content will be affected when the view is resized.

So as far as we know, to be safe and to be correct, we always have to redraw a view every time it's resized by a little bit, including during animation. So we do those kinds of things on the main run loop. We use, under the hood, Core Animation's evaluation engine for CA animation, so the results all come out the same, but they're all executed by AppKit on the main thread. Similar thing for all non-layer properties. Any custom properties you define for your views, as we'll see, that you want to make animatable, those are executed on the main thread.

So visually, what this looks like, if we have a single slide, it's actually composed of a set of four views, or actually we have two text fields at the bottom, so it's five. There's a slide carrier view that's the container view at the top. You have a standard NS button, a pretty standard image view, a simple subclass, a text field at the bottom that specifies the dimensions of the image.

So when we switch on layer backing, AppKit magically creates a layer tree that corresponds to that view tree. Each view gets a corresponding layer, and we wire those up together. So we have a set of four views, and we wire those up together. Each view gets a corresponding layer, and we wire those up together.

And that in a nutshell is the idea of Layer-Backed Views. Hopefully that's fairly clear as an overview. So now let's dive into some tips and techniques and some concrete tips that I hope will help you in dealing with various issues. We'll look at layer management and sort of the life cycles of layers and how layers come and go. Some people find these things surprising.

AppKit does manage layers and they appear and disappear and are associated and dissociated from your views in ways that can be surprising. But if you know how to handle that, it's not a problem. We'll look at text rendering. In Layer-Backed Mode, people have been surprised that text rendering doesn't quite come out the same. And why is that? We'll look at that.

Driving motion programmatically, a simple tip but a useful one. We'll look at some performance, drawing and debugging tips. And then we'll dive into a couple more lengthy and in-depth sections where we'll talk about how to sequence animations. People often ask, well, how can I make animation A go first and then animation B start after that, animation C? And that's very easily done using CA animations. And we'll look at how to do that.

And then lastly, we'll take a look at NSView's geometry model and how it compares to CA layers geometry model. And that's something that can be useful to know a little bit about when you're trying to figure out what kinds of keys to hang your animations off of and that sort of thing. We'll see that when we get to it.

So first, let's talk about the life cycles of layers. And ideally, we want you to have your views able to operate sort of agnostically. They can work in layer-backed mode and they can work in non-layer-backed mode. And for the most part, for general simple views, you won't really need to worry about which mode you're operating in, hopefully, because AppKit will take care of all the details of mapping your view into a layer. Leo Parmigiano-Rossi But occasionally, maybe you want to take advantage of, maybe you have a special view where, okay, you're drawing some items and if you're in layer-backed mode, well, hey, you can use a Core Image filter to provide a nice highlight effect on the selected item.

Leo Parmigiano-Rossi And that avoids having to redraw the item when you want to select it or deselect it because you just apply the filter and it's all done on the GPU. Leo Parmigiano-Rossi But you want your view to also work in non-layer-backed mode if people are using it in that case.

So you might want it to be able to adapt to being in layer-backed or non-layer-backed mode. Leo Parmigiano-Rossi You also might want to be able to add sub-layers of your own or set custom layer paths. Leo Parmigiano-Rossi So how does this work? To understand this, we first need to understand that there are two basic modes of Core Animation usage that we support in AppKit.

Leo Parmigiano-Rossi And the first one listed here is the one that we're talking about primarily in this talk, layer-backed views, where you have a view tree, you flip on layer-backing, AppKit automatically creates and manages the backing layers for you and for the most part, you don't need to deal with them at all.

We also support a standalone mode where maybe you have a portion of your user interface where you just want to work with Core Animation layers directly. You're not building user interface where you need dials and sliders and buttons and things like that. You're showing some graphics or an animation that you want to present and you just want to use CA layers directly. You may have a layer tree you want to render.

Leo Parmigiano-Rocco So the standalone mode is a mode in which you can simply tell a view, I want you to just be a canvas for rendering and animating this layer tree. That's all I want you to do. I'm going to give you the layer tree. You construct a layer tree yourself.

You populate it with content yourself. You set up all the geometry yourself. Leo Parmigiano-Rocco You tell the view, set layer. Here's your root layer that I want you to render. And then you tell the view after that, set once layer, yes. And that flips on AppKit layer tree rendering, but we don't otherwise interfere with the layer tree. Leo Parmigiano-Rocco The root layer will be resized to Leo Parmigiano-Rocco fit always in the area of the view when the view resizes, but all the other layers are left alone.

And so that's for using core animation directly. And the reason this is important to understand these two different modes of usage is that AppKit treats the layers that it creates and manages for you differently from the layers that you give it. Leo Parmigiano-Rocco If you give us a layer, we don't want to interfere with any of its properties. You may have set content on the layer. Well, if we set needs display on the layer, your content will disappear and we don't know how to replace it.

Leo Parmigiano-Rocco We generally try not to interfere with the properties of layers that you give us. But when we create the backing layer, we assume what we can mark it as needing display and you want us to draw view content into it. It's a view backing layer. So we'll do those kinds of things. Anytime the views geometry changes, as I said, we push those changes to the layer. Leo Parmigiano-Rocco As I said, the root layer of a standalone layer tree, this happens to also. For backing layers again, AppKit will destroy and recreate views backing layers at will to manage resources.

Leo Parmigiano-Rocco Remember, every pixel that your view covers is a pixel in that views backing layers backing store. So we're allocating lots of graphic memory, lots of texture memory for all these view backing layers. And if we figure out that, well, your view is not visible anymore, it's been pulled out of the window or something, we want to reclaim those resources for the system to use. Leo Parmigiano-Rocco So this kind of thing can happen. You can lose your backing layer when, say, someone does set wants layer no on a view higher up in the hierarchy. Maybe you had an ancestor that had wants layer set to yes and your whole subtree.

was drawing layer back. Now that gets it to know and all those descendants of that view cease to become layer backed. We'll pull the backing layers off of them. This can also happen, as I said, when you pull a view subtree, when you're pruning and grafting, you pull a view subtree out of window by doing remove from superview, backing layers can be torn down then too. And one common case where this happens is if you have views in a tab view and there are subviews of the tab view and they're layer backed.

If the user switches tabs, it just so happens that the mechanism that's used for doing the tab swapping is to prune and graft those view subtrees into and out of the window. So when you user switches tabs, oh, your backing layers might go away for some of your views that were in the tab that they switched away from.

Likewise, if you added properties or sublayers to an AppKit-owned view backing layer, these things can disappear unexpectedly because, well, the backing layer goes away and there's nothing referencing those sublayers anymore. So, therefore, you tend to need a way, if you are going to be going in there and you can access a views layer with the layer getter method, you can go in there and tweak layer properties. If you're going to do that kind of thing, you really need a way to react to your backing layer coming and going if it's one that's managed by AppKit.

So, how can you do this? One of the first things that developers try is, you know, it makes sense, override setWantsLayer because that's what switches this on and off, right? Well, it turns out that this doesn't work so well, and the reason for that is that while setWantsLayer is recursive in its effect, when you do wantsLayer yes for a view, it affects the whole subtree, setWantsLayer itself as a method is not invoked recursively. So, if you're a descendant of a view that became layer-backed, well, you're becoming layer-backed too, but you're not going to get the setWantsLayer yes message. So, that doesn't work out so well.

How about Set Layer? Well, it turns out this is an excellent place to handle layers coming and going because this is, in addition to being the public API that you use to provide a standalone layer tree to AppKit, this is also the funnel point that AppKit goes through when it associates a backing layer with a view and when it pulls that backing layer off.

So if you override Set Layer in your custom view class, call up to Super Set Layer to let it do its thing, and then you can look at the new layer parameter that's coming in. And if it's non-nil, well, then you're becoming layer-backed or maybe you're getting a different, you should be prepared to have had a layer before, but now you're getting a different type of backing layer because AppKit might need to substitute a different time, a different kind of layer. There are different kinds of layers for different kinds of rendering. Let's say your view decides it's going to start rendering using OpenGL, it associates in this OpenGL context. We might in the future say, okay, we're going to tear it down.

We're going to tear down that CA layer and give you a CA OpenGL layer. So you may get a layer where you didn't have one before, you may get a different layer, or you may get a parameter of nil, meaning that your view is ceasing to be layer-backed and we're tearing the layer down.

So overriding Set Layer, this is an excellent place to respond to all of these things because whenever a view acquires or loses a layer, the view gets a Set Layer message. It doesn't matter what triggered the mode switch, so it's a great place to set up your additional properties or sublayers. So the take-home point, override. set layer to do this kind of stuff.

[Transcript missing]

To understand why that is, we'll need to look at the two basic kinds of text anti-aliasing that Quartz supports. First, we have what I call whole-pixel anti-aliasing that treats each pixel as a discrete unit. And then we have the more advanced sort of sub-pixel anti-aliasing, also called LCD anti-aliasing, because it relies on special properties of the LCD panels we all use now. This is also called font smoothing in the API.

So whole-pixel anti-aliasing, first, how does that work? Well, let's say you have a grid of pixels, you've cleared the background color white, and you've got a glyph that you want to rasterize into that grid of pixels. You want to draw a letter A in black. Well, with whole-pixel anti-aliasing, again, we treat each pixel as a unit, and what the graphics system does is it computes the partial coverage of the glyph on each of these squares.

And if 70% of the square is covered by a piece of that ideal vector glyph, then you'll get 70% of the foreground color blended with 30%. And you end up with sort of a nicely, mostly nicely smoothed glyph there that when you look at normal magnification, it looks a lot smoother than the old jagged text we used to get a long time ago. So that was an improvement on alias text.

But then on OS X, the graphics system added the concept of sub-pixel anti-alias text. And there, let's say you have the same situation. You have a white background, you have a black glyph that you're trying to rasterize on top of it. But in this case, the graphics system knows that it can treat... Each pixel is actually being composed of three discrete components that are adjacent to each other and not overlapping.

So in each pixel cell on an LCD display, you have the red component occupies the left-hand third, the green component is in the middle third, and the blue component is in the right-hand third. And effectively, for anti-aliasing, this triples our resolution. If we look at each of those components as if it was almost a separate pixel, now we can anti-alias with triple the horizontal resolution. And what this ends up looking like to users is not quite the eyesore there. It's more of all the white pixels sort of blend together to white where R equals G equals B equals one. That sort of blends out to white when you're looking at normal magnification.

And for the areas around the edges, you end up getting these sort of color fringes that result from different amounts of blending of the red, green and blue because we're computing different coverage values. But when you zoom out, if you go across the street and you look at this, it looks like a nicely anti-aliased glyph.

So that's all well and good, but why is there a problem with doing this in Layer-Backed Mode? Well, the situation, the problem there is the same thing that's the benefit for doing animations. In Layer-Backed Mode, again, each view is drawn into its own separate layer rather than being commingled with the drawing that other views have drawn into the Window Backing Store. And this is wonderful for animation, but it turns out it doesn't work out so well for text anti-aliasing.

And the reason for that is that subpixel or LCD anti-aliasing relies on having an opaque background to draw into at compositing time. And to reiterate that, there really is no way, no good way that you can perform high quality subpixel anti-aliasing without having an opaque background, unless say we added two more alpha components and we'd have six samples per pixel and a whole lot more graph, about 50% more graphics memory usage. So really you need an opaque background. You need to know your background at draw time.

Well, in Layer-Backed Mode, I mean, we might have a text field that draws text into itself, but it's not drawing the background. It's the window or some other view higher up that's providing the background. And the background only gets in there later when the layer tree is fully drawn and then finally composited as the last step.

So, like I said, many kinds of views and controls do no background drawing of their own. Unfortunately, making matters a little worse, LCD anti-aliasing or font smoothing is switched on by default in the graphics context that you get when you're asked to draw. So the graphics system will happily do its best to try to do subpixel anti-aliasing into a transparent background if that's what you have. And unfortunately, to some eyes, those results can actually look worse than if you were just attempting whole pixel anti-aliasing.

So, okay, we get it. There's a problem. How do you solve this problem? Well, there are a couple of different approaches. If it's acceptable to you to put an opaque background in your view, that's the simplest way to do this. So you give the text a solid background to draw into.

If you have something like an NSTextField, this is real easy because NSTextField has a set background color property. So you can set the text field's background color in code if you want. Be sure to also set the text field to draw its background or it will just ignore that background color that you just set. And you can also do this in Interface Builder without writing anything. any code.

For other types of controls that show text but don't have an analogous background color property, one thing you can do is subclass the control or view, override Draw Rect, and provide your own background drawing. And that's as simple as at the top of your Draw Rect, filling with some background color that you want. It doesn't have to be window background color. It's whatever you know you're going to be compositing over later.

Do an NS Rect fill. If you saw Ken's talk earlier this morning, you might think I'm going to get in a little bit of trouble here with him because I'm telling you to use a Rect fill that uses basically a replace operation, a copy operation. But that's okay here because the layer owns its own backing store.

We're really replacing the pixels in the layer. We could do source over two because it'll be cleared to transparent. It doesn't matter either way. But we're putting a solid opaque fill in the backing store before we call up to Super Draw Rect, and that way you will get subpixel anti-aliasing.

Well, what if that's not an option? What if you're going to be moving your view around over a complex background and you don't want it to be carrying this big slug of color with it that makes everything look awful? The alternative then in those cases is to fall back to whole pixel anti-aliasing.

So what you can do is turn off font smoothing, and this is a similar kind of thing where you can subclass your viewer control, override Draw Rect, and use the CG context set should smooth fonts call to message the CG context. So you can use the CG context that you're drawing into, tell it, I want to turn off font smoothing, and then call up to Super Draw Rect.

And at least then you get better looking whole pixel anti-aliasing. So unfortunately, this is other than the slide we had before, this is a little difficult to illustrate without being on a machine where you can actually look at this magnified in Pixie. But we're providing a subpixel anti-aliasing example.

It's a real simple app that allows you to turn layer backing on and off for a set of checkboxes and text fields. And some of these checkboxes and text fields are going to be in the layer. And some of these checkboxes and text fields in the example implement some of the techniques that I just mentioned. So I encourage you to take a look at this, look at the rendered results with Pixie and compare between layer backed and non-layer backed mode to understand how this all works and how you can make your text look better.

So after that complex discussion, let's have a real quick tip. One of the things people often ask about is, how can I drive motion programmatically? Core animation gives you these ways to express canned animations, these sort of pre-planned paths of motion for different types of properties. But what if you're hooking things up to a game physics engine and you're simulating gravity? Or you really don't know what the position of an object is going to be until you calculate your next simulation frame or whatever? Well, this is really easy to do.

And it barely merits a slide, but it's worth pointing out because people often ask about it. Because even when you're not using animations, you can get a lot of benefit from being layer-backed or from using layers. If you're using layer-backed views, the answer is simply, well, set the view's frame origin, frame rotation, other properties immediately. Don't go through the view's animator, which is the usual means of initiating an animation, but just set those properties immediately.

If you're working with standalone layers, that is, working with CA layers directly that you've created yourself, the situation is sort of the reverse. Whereas for views, the default is to not animate. With core animation, you get implied animations by default. So you need to actually disable actions. This is the means of suppressing implied animations in core animations.

Disable actions in the enclosing transaction, and then you make all the layer property changes you want before you commit the transaction. In either of these cases, even though you're not using the animation capabilities that are built into core animation, you still get the benefit of content caching. Your content is pre-rendered. It's not going to be redrawn. You're just moving stuff around, so you still can get very smooth, fluid animations. So it's worth pointing out that you still get some benefit even if you're not using the animations, if you're just moving stuff around that's static.

So what about performance tuning? What can you do to get the best performance out of your Layer-Backed View portions of your user interfaces? One of the most important things to point out is that it's important to minimize backing store use in layer-backed mode. And this is very different from sort of the normal mode you're used to with views in a window where only the window has a backing store.

So you can have, say, a text field that displays one word, but it's got a lot of slop in it. It's stretched across the full width of the window, has more room than it needs. Well, the problem with that in layer-backed mode is that that view is going to get a backing store. It's going to get a layer that is sized exactly to the frame of the view.

So you may have pixels that aren't being used there, but that are nonetheless allocated on the graphics card, and you're wasting resources there. So with layer-backed mode, it's more important than it has been before to crop your content tightly, make your views crop as small as you can around the actual content that you need to draw. Try not to leave empty space in the margins.

Avoid unnecessary redraw whenever you can. And ways to do this include, as I said, avoiding resizing. Obviously, sometimes you need to resize a view in your UI. But if you can avoid doing it where you don't need to do it... that will make animations a whole lot smoother.

If you're just moving stuff around, that's really fast. But as I said, if you're resizing views, including using animations, and they're layer-backed, well, we have to assume that we need to potentially redraw the view at each step along the animation. That's going to be done on the main thread. It's going to occupy view drawing machinery. So it's going to be a bit of a heavier load on the system. So for now, try to avoid resizing.

Try to isolate static parts of your user interface. Things that don't need to be redrawn. Take the dynamic parts. I guess the flip side of that would be to say, isolate the dynamic parts, the parts that you're going to need to redraw in their own views, so that you're just redrawing minimally.

[Transcript missing]

Another thing to do, and this ties into a new feature that we've added on Snow Leopard that's beginning in your seed, consider enabling your views to draw concurrently. We've been looking at, of course, what's been happening with CPUs lately, and we have machines with up to eight cores now under our desks or on our desks.

We want AppKit and the system as a whole to be able to leverage concurrency more effectively and the ability to use multiple cores. So it doesn't make a whole lot of sense to force all of the view hierarchy to draw just on the main thread anymore because there may be other cores that are idle and can help with that.

So we have a new canDrawConcurrently property that's been added on NSView. So you can flag individual views as being able to draw concurrently, and this gives AppKit permission to call your view back, to call its drawRect back on a background thread. So you're saying, my view, you can safely invoke my drawRect. There's a bunch of information about this in the AppKit release notes.

What you need to do, what are the requirements? We've tried to make them as simple as possible for your views to be able to be considered thread safe for concurrent drawing. This is not leveraged yet in layer-backed mode in the seed that you have, but it is very likely that layer-backed mode will take advantage of this in the future. On the one hand, when you think about it, it's less of an issue because you're ideally doing less redraw in layer-backed mode, right? We're reusing existing content, moving it around, recompositing. So there's less overall redraw to do, so maybe less important to do it concurrently.

But then on the other hand, we're drawing views individually into their own backing layers, so there's no longer this order dependency. The compositing happens as the last step, so we can execute drawRects in any order we want to, so we have a lot more flexibility in layer-backed mode in terms of how we delegate things to different cores. So consider using canDraw concurrently.

Typically, as the advice in the AppKit release notes says, we expect you'll want to just select a few views that you have that do extra. So we're going to use the canDraw component to do that. And then we're going to use the canDraw component to do the other expensive drawing that have complex drawing they need to do and make those candidates.

What about drawing? The rules for drawing are mostly very similar in Layer-Backed Mode, but there are a few nuances to be aware of. One is that the semantic difference between sending set needs display to one particular view and sending set needs display to some ancestor of the view or an overlapping sibling, this didn't really matter before in Non-Layer-Backed Mode.

If you mark a view as needing display, in Non-Layer-Backed Mode, you know, effectively you're saying, well, the part of the window that this view touches is going to have to be redrawn, and the AppKit view system is going to redraw all the views that it has to that touch that area of the window.

Well, in Layer-Backed Mode, again, each view is drawing into its own layer, so we're only going to redraw the backing layer or the portion of the backing layer for the view that you marked as needing display. So be very specific about which views you want redisplayed and which portions of those views need to be redrawn. Use set needs display and rect when you can. It's always a help.

Pixel alignment works a little bit differently in Layer-Backed Mode. In Layer-Backed Mode, layer content is drawn, buffered, axis aligned in the view's own local coordinate system. And this is important really for when you have views that are rotated. If you use the frame rotation or the frame center rotation property on views, you've got a view that's rotated in a window. Well, normally with non-Layer-Backed Mode, you'd end up, if you drew a horizontal line in your view, that would end up rasterized sort of stair-step diagonally in the window backing store.

Well, when you're drawing to a layer, you're axis aligned as far as you're concerned when you're doing your own drawing. And then that drawing is rotated subsequently and composited in like an image by Core Animation's compositing system. So, axis, so, sorry, pixel alignment rules are a little bit different there. We provided some new pixel alignment helper API.

These aren't specifically for pixel alignment, but they get you to where you need to be to do pixel alignment. These were added in Leopard. We have convert rect to base, convert point to base, convert size to base. And what they do, and then there are the corresponding from methods, convert rect from base, convert point from base, convert size from base.

And base space is basically defined as a space that is reasonable for you to do pixel alignment calculations in. In other words, if you round to integral values in that space, you will get pixel aligned drawing. So, for Layer-Backed Mode, well, base space is the layer's own backing store space, the actual pixels in the layer backing store.

For non-Layer-Backed Mode, it's going to be the window's backing store space. And the nice thing about these convert to and from base methods that are on NSView is that they do the right thing, whether or not you're in Layer-Backed Mode, and you don't need to worry about that. So, I encourage you to use those. Okay.

What about debugging? What do you do when things aren't quite going the way you expected, the animations that you set up and were all ready to see didn't quite show up? Well, we have some tips to help you debug those kinds of situations. I'll be talking about some private elements that are not part of AppKit's public API, but that are things that are nonetheless useful for debugging.

So, don't depend on these in your code. Absolutely don't try to parse their output because the output could go away or it could change at any point in time. But this stuff has been there since at least Leopard, so it may be useful. One that's useful with Layer-Backed Views, potentially, is NSDebugLayerAnimations.

This is a user default. It's a Boolean. You set it to yes, and what it does is it will have AppKit automatically log out indications of animation for key and default animation for key. So, these happen when AppKit or CoreAnimation is searching for an animation to use for a property change. That's sort of how the model works for implied animations.

You change the property and then AppKit or CoreAnimation calls you back and says, well, okay, you want to change this property. Which animation should I use or should I use an animation at all to change this property? So, NSDebugLayerAnimations, yes, is useful for that, potentially. Alternatively, that can create a lot of spew because it'll give you output for all of the views that are being asked for animations in your application. So, you might just override.

You could override animation for key, which is the funnel point that AppKit uses for searching for a view's animation. That's a method on NSView. You could override that and, for your specific view class, provide some logging in there. NSDebugLayerActivity logs out information about the pushes that I talked about when a view property changes and AppKit figures out, okay, we need to update the corresponding layer property or when we need to redraw a view content to the layer every time that happens. So, like all other user defaults, you can use these on the command line when you're launching your application.

So, you can use this in the command line, you can use this in the command line when you're launching your application. You can use this in the command line when you're launching your application. You can use this in GDB using setargs and the same sort of dash syntax with the value afterward. And if you're using Xcode, you can open up the inspector for your executable and there's a nice little list that you can specify a bunch of different arguments you want and check them on and off as you're doing debugging or not wanting debugging.

What about when you want to take a look at what the geometry of your view tree looks like in the corresponding backing layer tree? Well, there's a subtree description method that's been in there actually since Tiger. And again, this is something not to rely on in your code. This could go away. Its output could change. Please don't parse the output or rely on it because your code will break.

But when you're in there in GDB, it can be very useful. And this works for non-layer-backed views as well as layer-backed views. So let's say you have a pointer to some view. A subtree description is analogous to the public description method. It's a debugging method. It returns you an NSString. So if you print out the value of the subtree description, you get sort of a one line per view. If you're not on a wrap display, you get a one line per view sort of summary of the view hierarchy with indenting indicating nesting.

The following slides are a little bit of a cheat sheet at the bottom that helps you interpret some of the flags. Look at what all of this consists of. How do you interpret this stuff? First, there's a set of square brackets. Each view corresponds to one line in the output, so we're looking at the line for one view. There are various Boolean flags. Here we have a view that has a capital W that tells us that it's set as wanting a layer, and it also has a hardware surface that it's rendering into, which is consistent with layer-backed rendering.

If the view auto-resized its subviews, its auto-resizing mask is indicated next. The horizontal and vertical components, where a dash indicates a rigid component and an ampersand, think of it like a spring in the old interface builder UI. It indicates a flexible space. You'll see the name of the class and an instance pointer to the view, so if you want to inspect it further, you can use that pointer, copy and paste it into GDB's command line. You'll see the frame for the view, origin, width, and height.

The bounds for the view oftentimes will just be indicated as a dash, because the case for most views is that a view's bounds, unless you explicitly set a bounds for a view, the view's bounds track the view's frame, where the bounds is implicitly has an origin of 00, a width equal to the frame width, and a height equal to the frame height. So you'll see that oftentimes, but if the view has an explicit bounds, you'll see it displayed there. If the view has a backing layer, if the view is in layer-backed mode or has an associated layer, you'll see an arrow next.

And then the class name and pointer of the view backing layer. You'll see the position of the layer and its bounds, and it's linked to its super layer. In this case, it's the top level, so it has no super layer. So subtree description, underbar subtree description, remember this, it can be very useful for debugging both non-layer-backed and layer-backed cases. And of course, remember that it's private. For when you're working with either view backing layers or just a raw core animation layer tree, a standalone layer tree, there's also a debugging facility. And if you're working with a raw core animation layer tree, there's also a debugging facility.

Core animation is particularly useful. This is an environment variable, not a user default. If you set ca print tree, that's ca underscore print underscore tree, all uppercase, set that to one in your environment before you run your app, core animations render thread will dump a description. Each time it renders a frame, it's going to tell you the time it's rendering at and the bounds of the root, and then it'll give you a similar sort of subtree description of what does the layer tree look like. So you'll see.

Things like I won't go over all of this, but you'll see the position of the layer. And remember, it has X, Y, and an optional Z position that's usually zero. It's always zero for view backing layers. Layers bounds, the layers anchor point. We'll talk about that in a bit. So those are all useful, hopefully useful to you techniques that you can use for debugging stuff and seeing what's going on there when what you expect isn't happening.

Sequencing Animations: How can I put multiple animations together? How can I know when an animation has completed? Core Animation provides two powerful CA Animation subclasses: CA Keyframe Animation and CA Animation Group. CA Keyframe Animation is a group of animations that work together to create complex and intricate animations. We'll look at CA Keyframe Animation first briefly. A keyframe animation animates a single property through a sequence of waypoint values that you define.

I want it to go from here to here to here to here. When we hear keyframe, we think of frames and positions, and it does apply to that kind of thing. If you have a view or a layer and you have a sequence of positions that you want it to go through, you can use a keyframe animation to have it go from 0 to 1 to 2 to 3.

It also applies to other types of properties as well. Maybe you want to animate the view through a series of angle positions, or maybe you want to change its color through a series of colors and have it interpolate through those. These are all the kinds of things that you can do, for example, with keyframe animations.

For a keyframe animation, the key path names the property that you want to animate. So if you're working with layers directly, you'll be naming a layer property here. But when you're working with Layer-Backed Views, remember it's a view property that you're animating, that you're specifying. AppKit will automatically try to translate that key to a corresponding layer property to be animated.

The values array on CA Keyframe Animation specifies the waypoints that I described. These are the points that you want to hit along the way as you're animating. You can optionally specify an array of corresponding key times, of the same number of key times as you specified values, and that'll say, well, here's where in time I want you to hit each of those values.

You can also specify timing functions. If you have n values and key times, you can have n-1 timing functions that specify what sort of acceleration curve should be used in moving between any particular pair of points. So you can specify an ease-in, ease-out curve is a popular one, that kind of thing.

And there are some other optional properties that you can use as alternative ways to specify how the value will change over time. But those first ones are the main interesting ones. But again, with CA Keyframe Animation, we're talking about animating a single property through a sequence of values.

So what if you have something more complex you want to do? You want to animate multiple properties of an object, maybe color, position, orientation. You want to animate multiple objects at once. How do you handle that kind of thing? Well, that's where CA Animation Group comes in. And it's a very simple yet powerful construct that allows you to arbitrarily aggregate animations together.

You can have animations of various types, including other animation groups. So these can be nested in a hierarchy themselves. You can animate various properties of all kinds of different types, colors, geometry, that sort of thing. And you can animate multiple target objects at once with an animation group, because again, you're just gathering up a bunch of other kinds of animations and executing them together. CA Animation Group, like Keyframe Animation, is a subclass of CA Animation. So it can be used anywhere in the API that a CA Animation is called for.

There's one important property for CA Animation Group. Well, maybe two. The main one is the Animations property, which provides the animations that you're giving it. Okay, I'm gathering these up, make these into a group. Animations is typed as an NSArray, but really it almost might as well be an NS set because the order of the constituent components is not significant.

What is significant is the timing properties that are set on those sub-animations. It's the timing properties that specify where each sub-animation gets executed, when it starts, what speed it goes at, what duration it runs for. And these are the eight timing properties that are defined by the CA Media Timing Protocol that all CA animations conform to.

The other interesting property to note is the group's duration is important in that whatever sub-animations you put in there, it doesn't matter what their begin times and durations are, they will be clipped to the duration of the parent. So when the group duration has expired, none of its constituent animations will continue to execute. They're all done at that point.

Timing Model: These are the eight basic and not-so-basic timing properties that you can use to have animations be driven in various kinds of ways. There are four in particular that are interesting to focus on. Duration and begin time are probably the two most basic. When the animation starts, how long it runs for. These are both in seconds.

Optionally, you can also specify a time offset and a speed that work in this sort of linear equation to map local layer time to animation time. But most of the time, your time offset is zero. Your speed is 1.0, so this collapses to basically you're figuring out how far along you are in the animation linearly with no scaling. So duration and begin time, those are the real important ones to pay attention to as we're looking at some examples.

Okay, so concretely, let's say we have an animation group. We want to execute an animation for one second. And simplest case, we have, let's say, two constituent animations that we want to put in those groups. And we want those all to start together and execute for the same one second duration. How do we set this up in code? Well, let's say we've allocated a CA animation group called group.

We set its animations property to an array of consisting of anim two. Specify a duration for the group. Remember to do that. That defines the duration it's going to run for. And then let's say anim one, we want it to begin at time zero, the beginning of the group. The group has its own local time. So time zero is the beginning of that group.

We want it to execute for one second. Same thing with anim two. Begin at time zero, execute for one second. And that's how you would set up simultaneous animations. Okay. So let's say we have an animation group. We want it to begin at time zero, execute for one second. And that's how you would set up simultaneous animations. And that's how you would set up simultaneous animations. Okay. Okay. Okay. using groups.

So this is very powerful in that groups aren't just sequences of animations, you can do all kinds of things with them. You can have an animation proceed after another animation. Let's say we want Anim1 to run for three quarters of a second, we set its duration to 0.75. Anim2 should start at the end of that, so we set its begin time to 0.75 and its duration to 0.25, so it runs for the remaining quarter second of the group.

You can do all kinds of other things like overlapping animations, so having them execute partially together. You can have gaps in between animations where for a quarter of a second here we have nothing happening after Anim1 completes and before Anim2 starts. And again, the scales to arbitrary numbers of animations you want to put into the group.

So animation groups, very powerful constructs, and also keyframe animations, very powerful constructs. So what's the crucial last step? Once you've built your animation group or keyframe animation, how do you use this? There are a couple of different approaches you can use in the AppKit API. There's the per instance approach and the per class approach.

If you have a particular view instance and you just want that particular instance to fire off the animation you've constructed when one of its properties changes, let's say here we have the frame origin property of view. When that changes, we want to fire off this animation group we've constructed.

You use the set animations method. Each view has an animations dictionary. Each view instance has its own dictionary of animations that maps property key names to. So if you have a custom view class and you want all the instances of your custom view class to do the same thing when, say, frame origin changes, you can use the set animations method.

A better thing to do is to override the class method default animation for key on your view class. And you'll look at the incoming key and say, hey, is that the key I'm interested in? Is it frame origin? Okay, well, if so, I've got this complex animation. Maybe it's keyframe animation, group animation, whatever that I want to be applied to change the frame origin or when the frame origin is changed. Always call up to super if you don't recognize the key. This is the chaining mechanism for default animation for key so you can inherit default animations from your super classes.

So lastly, how do we know when animation's complete? Simple question with a surprisingly simple answer, or actually a set of different answers. If you really want to get a callback when your animation completes, and you happen to be providing your own CA animation instance already, like when we created our own group or keyframe animation, well then, you can just easily set the delegate of that animation. Each animation can have a delegate. And the delegate, if it implements these methods, will get animationDidStart and animationDidStopFinished. It begins executing and when it terminates.

But alternatively, if that's not convenient, remember that you can also rely on animations completing in wall clock time. All of Core Animation's facilities are designed to be scalable to system load. So the system can be really loaded down, but if your animation was set to execute for three seconds, well, when three seconds of wall clock time go by, it's going to be done.

Troy Stephens So this means that you can just use something simple like a timer to call you back, an NS timer or other equivalent OS facility to give you a poke when the animation duration has elapsed. And often that's actually simpler because then you don't have to mess with creating your own animation when you wouldn't otherwise have to.

And then, of course, remember in the context of looking at groups and keyframe animations, in many cases, the ability to sequence animations, if that's all you want to do is have animation A happen before animation B and then animation C, you can do that with groups. And you don't need to get a callback. You just set that all up in advance and let it fly. So we have a brief demo of that, just to give you an idea how that works.

This will also be available very soon for download as sample code. It is in the pipe. This is intentionally designed as a very simple example so that the code is not overly complicated, but we are animating three different properties of a view. We have this blue sort of canvas view that draws some guide points that tell us, well, we're going to take this orange sub-view and we want to move it through to point one, to point two, to point three. And you can see we're going to change the orientation of the view as we go along. So we're changing not only the view's frame origin, we're going to change its frame rotation as we go, and we're also going to have it change colors as we go.

So when we execute that animation... This is done using both keyframe animations and animation groups. I'm using a group as the top-level animation so that I can aggregate these different animations together. For each of the properties being animated, I'm using a keyframe animation. There's a keyframe animation for the frame origin that specifies where the corner of the view goes.

There's a keyframe animation for the scalar frame rotation property. And there's a keyframe animation also for the color property. One interesting thing to note, NSColor properties, AppKit didn't know how to interpolate those or animate them in Leopard, although there's a neat trick. If you link against the Quartz framework, there's a category inside Quartz Composer that provides that. So it'll actually work.

This is running on 10.5.3, by the way, on Leopard. As I think I meant to say before, all of these techniques that I'm describing today work on Leopard as well as Snow Leopard. So you can link against the Quartz framework to get NSColor animation support. On Leopard or in the Snow Leopard seed that you have, this is fixed.

It's an intrinsic part of AppKit now. So you have NSColor animation built in. And another interesting thing to point out is that all of this animation capability that we built into AppKit is also, even if you're using CA animations to describe it, you don't have to be in layer back mode to use it. So when I toggle off this layer back checkbox, now we're just running in conventional view drawing mode. And the frame rate maybe isn't quite as fluid, but it's pretty good. And I haven't changed anything otherwise about the way that the animation is specified and implemented. Okay.

So that is group animations and keyframe animations operating simultaneously. I don't have time to go through the code step-by-step on stage, but there's going to be a readme with it. It's all set to go. So as soon as that's available to download, I hope you will take a look at it and encourage you to examine it at your leisure. So we can go back to slides, please.

So, one more topic, last topic for today. Hopefully you left a little room in your memories for it. Layer and view geometry mapping. Why is this important? Why are the differences between NSView's geometry and a CA layer's model of geometry important? Leo Parad Well, it turns out to matter a bit because AppKit has to do some automatic mapping between view properties and layer properties, as I said, to implement that push model.

And we try to get it right most of the time. In some cases, it doesn't quite come out looking like what you might expect, and you might have animations hanging off of certain key values that don't seem to get executed. So, why is that sometimes? As I said, there are many similarities between the geometry models, but there are several important differences. So, let's take a look at what those differences are.

is the founder of the Let's look at this slide on accommodating those differences. As I said, we map view property changes to layer properties. Every time we change a view property, I'm probably... Every time you change a view property, we're going to map that to a change in one or more of a backing layer's properties. That's really the most important point on this slide, is that it's not necessarily a one-to-one mapping. We change a view property, sometimes we're going to have to change more than one layer property to get the corresponding look on the screen.

So what do these two models look like when we look at them side by side? First thing interesting to point out is that AppKit strives to be resolution independent, so all view geometry properties are specified in units of points, which are ideally a 72nd of an inch, whereas Core Animation doesn't really try to deal with resolution independence issues. You specify everything in pixel units always with layers.

Leo Parmigiano-Rocco So this will come up if you're looking at your Layer-Backed View tree and you use PO of your view subtree description and you look at the backing layers bounds and you'll say, well, if you're running at more than a 1.0 scale factor, you might notice that the layer is going to be sized numerically larger than the view. AppKit automatically does that to try to accommodate running at higher scale factors.

Leo Parmigiano-Rocco What about exterior geometry? For a view, the primary determiner of the view's size is its frame size. Leo Parmigiano-Rocco Whereas for a layer, it's really bounds that's a more intrinsic property. Bounds is what defines how many pixels wide and high a layer is, as well as what the layer's interior coordinate origin is.

For positioning with views, we use frame origin. Whereas for a CA layer, figuring out where the layer goes in its super layer, determining that is done by a combination of setting the layer's position and also an anchor point value that basically specifies, well, what part of this layer should I pin to that position in my super layer? Leo Parmigiano-Rocco We do have a concept of frame in the AppKit level for NSView, but really it's frame size and frame origin that are the more fundamental properties for a view. In that if you invoke set frame for a view, well, AppKit behind the scenes is just going through the public set frame size and set frame origin methods. Those are the fundamental components, and frame is really more of a convenience concept.

The same is true for CA layers. There's a frame property defined on CA layer, but that's really a convenience. When you set a layer's frame, Core Animation under the hood figures out that it's a frame. It figures out, okay, well, what position do I need to assign to the layer? What's the layer's new bound size going to be in order to get the same effect? So frame is really kind of a convenience concept.

In terms of other exterior transforms, you can rotate a view by setting the view's frame rotation. Core Animation exposes a bit more of a general model for CA layers. You can apply an arbitrary transform defined by any 4x4 matrix to a layer to scale and translate and rotate and do all of those things. with a single property.

Interior Coordinates. As you may know from working with views, views can be flipped. And when an NS view is flipped, that means its origin for its own drawing and for positioning of subviews is in its upper left corner. And its y-axis extends downward from the top. So positive y goes down.

This is convenient for certain kinds of views. But it doesn't really affect the views. It affects where the views, immediate subviews get positioned, how their frame origins are interpreted. But it's not recursive in effect further down the view tree. In Snow Leopard, C.A. Layer introduces an IsGeometryFlipped property that John Harper talked about in the Core Animation talks yesterday.

IsGeometryFlipped is a recursive property in effect. Sort of affects the whole layer hierarchy going all the way down. So it's not really exactly the same concept. It has a different purpose really, which is to accommodate... Porting your code that works with layers between the iPhone or iPod Touch world and the Mac desktop world where things are flipped differently.

So those don't really map to each other exactly. What about translation? When you want to shift the contents of a view, you've got more content than will fit in your view, and so you're just drawing a certain visible portion at a time. This is accomplished using bounds origin changes at the NSView level. You change a view's bounds origin, and you end up changing which rectangle of your content it ends up drawing.

Same thing in that case actually applies to CA layers. But it's important to note that bounds is an atomic property for a layer. It's not bound size and bounds origin that are the fundamental components, but bounds itself is one value that you can set atomically as a unit.

Scaling and other kinds of transforms. You can set the bound size for a view. You can set the bounds rotation. Bounds rotation hasn't been really useful to people over the years. It's kind of esoteric, but it's there. Again, in the Layer Kit land, or rather Core Animation land, sorry, you can apply more general kinds of transforms at draw time. It's not defined in the CA Layer API exactly as a property, but you can just apply whatever transforms you want when you're doing your drawing and hit testing.

So why is this important to know? Well, let's take an example where we have a Layer-Backed View, and we're changing its frame origin. And we're messaging the view through its animator, so we want to get an animation. is the founder of AppKit. He's the founder of the new layer that maps to this new origin. When the view is layer-backed, AppKit starts by mapping this to, okay, well, changing the view's frame origin means I've got to update the layer's corresponding position. So we figure out the corresponding position for the layer that maps to that new origin for the view.

[Transcript missing]

So that's what we have for today. In brief recap, we looked at layer life cycles and how to deal with layers coming and going. The take-home point there, override set layer. That's an excellent funnel point to find out when you're getting a backing layer, when it's getting pulled away from you, so you can adapt to that if you need to.

Subpixel anti-aliasing works differently in layer-backed mode. And the best solution there is usually to either provide an opaque background for your viewer control so that you get good quality LCD anti-aliasing, or leave your control without a background and fall back to whole pixel anti-aliasing by disabling font smoothing in the graphics context. We looked at how to drive motion programmatically, real simple. Performance drawing and debugging tips. Sequencing animations. Again, there will be a code sample, a sequencing code sample that should be available very soon.

You can inspect the code, and there's a readme that explains how it all works. That's the very same example that I showed here just now. And we looked at how layer and view geometry compare, and hopefully I didn't confuse you too much with that. Try not to let it hurt your head. But it's just good to know that things are a little different, and even where the same terms are used, things like frame and bounds, they don't necessarily quite mean the same thing, so be careful about that.

We will have a Q&A session briefly. We have about 10 minutes for Q&A after this talk. But for anything more complicated, if you have a very complicated issue or some debugging issue you need help with, I'd like to also point out there's a Cocoa Lab that started at noon and is continuing until, I believe, 1.45 today. I will be there immediately after this talk to try to help answer any more in-depth questions you may have.

So I encourage you to come. There were a couple of Core Animation sessions yesterday that were an excellent introduction to Core Animation and all the details of how it works. Those will serve you well, even if you're working with Layer-Backed Views. It helps to understand what's happening under the hood.

So when those become available on ADC on iTunes, I encourage you to look at those. We have three more excellent Cocoa sessions lined up for this afternoon, back-to-back. At 2 o'clock in Marina, we will have a Cocoa Performance Techniques talk that is all new. This is something that hasn't been presented before at WWDC.

And there's a bunch of new material in there covering new Snow Leopard features and techniques you can use specifically on Snow Leopard, as well as on earlier OSs for optimizing things like caching, purgeable memory, ways to exploit concurrency and ways that we provide concurrent APIs for you in AppKit and Foundation to enable you to leverage multi-core machines more effectively. Polishing your Cocoa application in Russian Hill today at 3.30.

And another performance session focusing on document-centric Cocoa apps. If you're using the NS Document. You'll want to go to Russian Hill today at 5 o'clock to hear about that. And I believe there will also be some talk about sudden termination in there, a new feature of Snow Leopard.

For more information about any of this, you can contact Derek Horn if you like. He's our Application Technologies Evangelist. I encourage you to read the documentation that we have and also keep an eye on the App Kit release notes for latest changes, especially starting with your seed because we've been working on stuff.

Troy Stephens Your feedback and bug reports matter very much to us. This is a technology that we're very interested in advancing further. We have some ideas in mind about things that we want to improve in it, but we'd like to know especially what's important to you, what kinds of issues have you run into.