Frameworks • iOS, OS X • 57:05
Animation plays an important role in apps for iPhone, iPad and iPod touch. Learn how to build high performance animated user interfaces using UIKit and Core Animation. Gain insight about various types of animations. Also hear about the best practices to follow when creating animations.
Speakers: André Boulé, Tyler Hawkins
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
[André Boulé]
Animations are one of the defining features of iPhone, iPod touch, and iPad. From the moment you unlock the device, almost every app has these beautiful animations all throughout. So, good morning, and welcome to Building Animation Driven Interfaces. My name is Andre Boule, and I'm an iOS Software Engineer.
And I'm proud to say that like many of you in the audience, I am an app developer. I've contributed to many of the built-in apps on iPhone. And most recently, I worked on the YouTube app for iPad. I think that this background as an app developer gives me a unique perspective on to the challenges that you guys face in bringing animation to your apps. I've been through it before.
And today, we're going to go through it together and we'll see how easy we can make it. Before I get into it, animated:Yes, if you can do just this, I mean that's pretty easy, just animated:YES, you can get animation in your app. And it's because UIKit has a rich set of built-in animations, this example UINavigationController.
When you push and pop a controller, you can do it in an animated fashion just by telling UIKit that you want animation. Another example, UITableView has very rich editing for inserts, deletes, and reorders. Also, again, you can just tell the system you would like these operations to be animated.
So, why do we use these animations, why do we provide you with built-in animations? Well, it's to provide context to the user. Animations are not just eye candy, they help explain to the user what is going on in your application. Now, of course, you always want to design your interface such that if the user is looking away from the screen, they're not completely lost, but because it's a touch screen and you're using your fingers, most of the time, when you're interacting with the device, you're actually using or you're looking right at the screen. So, animations help explain to the user what's happening in your application. When you use the built-in UIKit animations, your app is consistent with the rest of the apps on the platform. So, it makes your app easier to learn.
Some have said that the mark of a good programmer is being lazy. Well, I don't know about you guys, but if that's true, that makes me a pretty great programmer, so, it's less work for you. Now, that said, that's not why we're here today, right? We want to go beyond what UIKit provides and build our own custom animations. So, what are we going to cover today? The first thing I'm going to talk about is UIView Animations. UIView Animations is the basic UIKit API that you're going to use most of the time for building animations in your app.
We're then going to talk about transitions. But what are transitions? Transitions are animations between states or different UIs. So, two good examples that I like to give are in the Notes application when you're flipping from one page of text to another, that beautiful flip animation is the transition. And also, many of our apps have a frontView and a backView like the Stocks and Weather applications.
So, when you transition with the flip animation to the backView, that's also a transition. Next, we're going to talk about supporting window rotation animations in your app. It's very important that you support both landscape and portrait in your apps, your users would really appreciate it if you did. So, we'll talk about how to do that.
We're going to talk about performance. One of the things that makes the device so magical is when the-- animations have great performance, it almost looks magical. So, it's very important to have good performance. We'll talk about that. And finally, one of the questions that we get a lot from developers like you guys, is when should I use UIKit to do my animations? And you know, you probably been to some other CoreAnimation sessions or have read about it, when I should use CoreAnimation directly? Well, that's an interesting question, and we'll talk about that at the end of the talk.
So, we have a lot of content to get through, so I'm going to go right into it and talk about UIView Animations. As I said, this is the basic API that you're going to use in the vast majority of animations within in your app. So, if you've built animations before, you may have done something like this in the past. I have a view here that's completely transparent. Its Alpha has been set to 0, and my goal is to dissolve it to become visible.
So, I'm going to animate the Alpha of the view. I've created a timer, and each time my timer callback fires, I'm going to progressively change the Alpha of that view. Let's see what that looks like.
[ Pause ]
You have to manage, well, how fast do I want my timer callback to fire? Every time my-- your timer callback fires, you have to run some code to figure out what's the appropriate Alpha that I want to set it to, and actually set it. Really, all you care about when you're building your animation is that last step, right? Your view is completely transparent, and you want it to set it to Alpha:1.0, but you want that to happen in an animated fashion, why should you have to worry about all those intermediate steps? Well, the good thing is, that's how UIView Animation works, you don't have to worry about those steps.
You create your animations, set it up, and then once you fire it up, the system takes care of the rest and actually does the animation for you. So, same example, but with UIVIew Animation this time. myView has an Alpha of 0, and I'm going to set it to 1, and let the system take care of animating it in.
Nice. Now, one of the advantages here, without you knowing it, this-- without you knowing it, this actually happened in a hardware-accelerated fashion. Many people believe that in order to build a hardware-accelerated animated UI, you need to learn OpenGL or some other similar technology. With UIView Animations, that's just not true.
Now, you may have noticed the logo that I used-- that I'm using in my animation examples here, it's the CoreAnimation logo. And that's no coincidence. Because although I'm using UIKit and UIVIew Animations as an API to build these animations, UIKit is actually powered by CoreAnimation, so you're using CoreAnimation without knowing it.
So, let's look at the code that you can use to build the animation that I just talked about. And for those app developers out there who already have apps, you've seen this before, but we'll go through it quickly. We create an animation, tell the system we want it to be a two-second animation, and then make a change to an animatable property, in this case, the Alpha. Finally, we commit the animation and the system runs it, it's just that simple.
New in iOS 4.0, we have a new blocks-based API for building animations. So, if you're not familiar with blocks, I encourage you to go find out about those, but you should see here that this is an equivalent example using the Blocks API. So, with Blocks, you provide a block of code, and that code does some settable properties, it will change some values, and you'll get the same animation, but you get all the benefits of using Blocks, so it's pretty neat.
Result on the screen is the same as you can see. Alright. So, I've been showing some Alpha animations. Of course, any property that's animatable works just the same. In this case, I'm going to move the view on screen using a frame animation. I could also animate the center property of a view, and the result would be similar. If I want to rotate a view, I can animate the transform of the view.
You could set a scale transform, for instance, for a similar result. So, there are many animatable properties. And the header files will tell you which ones are animatable. And of course, I can combine multiple of these properties into one animation, and get two animations at the same time. In this case a frame and the Alpha animation.
What if I wanted to chain animations? Well, there's a more advance API that provides for you a completion block that you can pass in. What's a completion block? Well, that's a second block of code that's there in the blocks-based API. The difference is this completion block only runs after the animation completes.
So, any code that you put in that completion block will be triggered once the animation is finished. What have I done here? Well, in my completion block, I've created a second animation, so the result is after the first animation completes, the second animation begins. Let's look at that.
Alright. It's already you get the idea of it. Just with these basic things, you could do a lot in your UI. I want to caution you that not every property on UIView is animatable. In particular, what would it mean to interpolate between BOOLs. Half-yes, half-no, that doesn't really make sense, so don't try to animate BOOLs.
So, in conclusion, I mean I know we've only been here a couple of minutes, but with what you've seen so far, I bet you, you can implement most animations in your app. The API is extremely simple. What I've shown you is just the single view, obviously. But if you have subviews within your view, it will just work as expected and the subviews follow along. So, with just this, you're almost 90 percent of the way there. Of course, we want to go deeper. So now, let's go beyond the basics and look at some of the more advanced things that you can do with animations, I know that's why you guys are here.
So, if you want to cancel an animation, that's halfway through, how do you do that? Well, you can look, but you won't find a cancel method in UIView to cancel an animation. You don't need one. If you want to cancel an animation that's halfway through, all you need to do is set that property that you're animating outside of an animation block.
What happens? The property will just jump directly to the value that you're setting, and the rest of the animation will just not occur. So, it's just that simple. There's also a finished parameter in the new Blocks API. And that value will be NO to let you know that the animation didn't finish.
Alright. Next, we'll talk about animation options. I'm going to cover five different options, five concepts, really. I encourage you to go look at the header files to see how to use these in the old API and the new blocks-based API. But really, what I want you to leave with today is understand the concepts of how these things work. So, in the old API, there are setter methods. In the new blocks-based API, you can see these options that you can pass in as a mask, these animation options.
So, let's look at those five different things that I want to talk about, that's the full API for you if you care to remember it, but remember options. First one very simple, if you want to repeat an animation, you can use the repeat options to do this. You can either use repeat by itself, which will cause the animation to start back at the beginning and then repeat. Or repeat in conjunction with autoreverse which will cause your animation to go back to the beginning in the reverse order after it finishes. Pretty straightforward.
Alright, Animation and Interaction. I want to make a note here about how you should treat this in your app. You need to ask yourself the question, does it make sense for my app to respond to touches during an animation? What you may find is that in many cases you're transitioning, you know, an object to and from the screen, you don't want the user to be pushing on buttons on that UI while it's animating. So, by default in the new blocks-based API., app-wide interaction is disabled for the duration of the animation. So, you don't need to worry about disabling interaction.
The system takes care of it for you. That's said, there are probably cases in which you do want interaction during an animation. So, you can override this behavior using the AllowUserInteraction option. This doesn't apply to the old API because the old API did not get you this interaction disabling for free. Alright. Let's talk about animation curves. Animation curves are all about the timing of your animation.
Now, we've looked at this frame animation, you know, from left to right before, but let's look at it again. And this time, pay close attention to the way the object moves at the beginning and the end of the animation. Specifically, look for an acceleration at the beginning and a deceleration at the end of the animation. Alright. You're ready? Now, it might be more obvious if we look at it without that acceleration or deceleration.
You can turn off the curve using CurveLinear. In CurveLinear, the object defies the laws of physics and accelerates at an indefinite rate to its final speed. And then at the end of the animation, decelerates just as quickly. Let's look at them side by side, it'll be more obvious.
Cool, right. There's two more CurveEaseIn and CurveEaseOut. Now, it's your homework for tonight to go try those out yourself and see what they look like. Don't worry, it's a fun homework, you know, animations, it doesn't get any better than that, right? Alright. So, number 4 of 5, Begin from Current State. Again, this one, this is-- it's easiest to explain this by looking at an example. So, in my example here, my view is going to first animate top to bottom, and after that animation finishes, left to right.
OK. Simple enough, right? Now, imagine the same scenario except instead of animating left to right after the first animation completes, what if that second animation starts midway through the first animation, what would that look like? You may have a mental picture of what that would do, but let's look at it in reality and see what happens.
The view jumped from the midway point in the first animation directly to the endpoint, and then animated left to right as you would expect. But why is that? The reason is that in your first animation, you're not just animating the view top to bottom; you're actually setting the view's position to that bottom position.
So the result is when the second animation begins, it's going to animate from that position and not where the view is currently on screen, that's the default. If instead, you want the view to animate from where it actually is on screen, you can do that with the option BeginFromCurrentState. So, this time, it's the same example, but I've turned on BeginFromCurrentState for the second animation. Let's see what that looks like. That's great.
The view animates directly from where I want it and where it was on screen. But let's looks at it again, there's a slight pause right there where the view is accelerating to its final speed, and that's because-- well, we talked about this, right? The curve provides for an acceleration when the animation begins, we know how to turn that off, don't we? Yeah, you can use the curve option. So, same example this time, BeginFromCurrentState, but I've turned down-- I've turned on CurveEaseOut to disable that acceleration.
Nice. The view just continues to move down to its final position. So, I've shown this for a couple of reason, but one is to show that you can combine these options together to get the effect that you want. Alright. Last but not least, Property Inheritance. This one again is specific to the new Blocks API.
In this example, I've created an animation, and I've specified a two-second duration. What does my animation do? Well, firstly, it sets the Alpha of my frame to zero, but it also calls this shrinkFrame method. This is a method that I've implemented on my UIView subclass. OK, that's interesting. Let's look at shrinkFrame. What does that do? Well, shrinkFrame can be called from other places in my application. So, to make sure that shrinkFrame is animated, shrinkFrame is also creating its own animation. But what's this? Its duration is 1.0 not 2.0 like the other one.
Well, in your application, this might result in out of sink applications or animations where two different animations are of a different speed, not really what you want. Well, the system helps you out here again. And when you nest animation blocks one within another, the inner one will automatically inherit the duration of the outer one. So, although you've specified one, it will inherit that 2.0 from the outer animation and that is the duration that will actually be used. That's great.
But what if you actually did mean 1.0? You can disable this inheritance using OverrrideInheritedDuration. Alright. So, in this case, the 1.0 would actually be used because I specified OverrrideInheritedDuration. The same concept applies directly to curves. If you have nested animation, curves are also inherited. You can disable this inheritance using OverrideInheritedCurve.
Alright. That's the five advanced options that I wanted to talk about for the basic UIView animations API, repeat autoreverse. Consider interaction. And whether it should be on or off during your animations, the different timing curves that you can use, turning on BeginFromCurrentState when appropriate, and being able to override the inheritance of animation properties. Moving on, let's talk about transitions.
Now, again, as I mentioned in the beginning, some perfect examples of transitions are this one in Notes application. What's interesting here is that the UI is actually identical in both the before and the after of the transition. It's the same note view, the same buttons, everything is the same. What changes is the text on the note.
Contrast that with the Weather app. On the Weather app, we have two different UIs, the front and the back. And the transition, in this case, a flip is used to animate between those two views. So, two examples, you might have guessed it, we have two APIs to make both of those examples very easy to write.
The first one is called transitionWithView. And the API name implies that you're passing in one view. So, what do you do? Well, you make changes to your view, and the system takes care of animating between the before and the after of your changes. So, well, just remember this, transitionWithView, and then you pass in your view that you want the animation on.
This is the full API, but transitionWithView is the part to remember. Now, let's look at an example of using transitionWithView. This is the code flipToNextPage that could be behind the pushing of the Next Page button in the notes example. So, when I push this, my notepadPages object is going to give me the text as an NSString for the nextPage. I have a noteView which can display this note to the user, and I'm going to set the nextPageText on the noteView.
Finally, after I've made the changes to my view, I'm going call tansitionWithView and pass in the noteView. You'll notice in the options here, I've said I want to CurlUp animation, and that's it. That's all it takes to get that CurlUp and that transition from one page of text to another. I want to make a special point here. What did I do? I just moved this set text line. It was before the transitionWithView, now it's after.
What's the difference? Well, there is none. You should know that any changes that you make during the current core animation transaction, we haven't talk about that yet, so unless you've created one explicitly, that would be the current runloop. Any changes that you make in that transaction would be reflected in the animation.
The system is going to take a snapshot of what you're view used to look like, and then you make all your changes and then you'll get that transition to the new state. So, it doesn't matter the order that you make your changes in here, beautiful CurlUp animation nice. Alright. So, the second API for doing transitions, transitionFromView: toView: As you can see by the name of the API, it takes two different views.
You use this when you want to animate between the two views. The system will take care of removing the first view from your view hierarchy and replacing it with the second view that you pass in. So, again, remember transitionFromView:toView: Easy to see how this works in an example. This example is even simpler than the Notes one. This is my flipToWidgetBackside method which is invoked when the user presses the Info button on the front of the Weather app. When this happens, I have a contentView, and the contentView has the frontView as a child.
All I need to do to get that flipped to the backside is called transitonFromView: pass in the frontView, toView: pass in the backView. And then after that transition completes, not only will it animate with that flip, but after that animation completes, the content view will contain the backView as a child because the system automatically replaced it.
What does that look like? Nice flip, alright. So, you can see transition very simple, two different APIs, one that takes one view that you make changes to, and the second API where you provide both views and you get a transition between both of those views, just that easy. Right, we've talked about a few things here. I'd like to invite my coworker Tyler Hawkins up to give you a demo of what this could look like in your application.
[ Applause ]
[Tyler Hawkins]
Thanks so much. So, yeah, my name is Tyler Hawkins. I'm also an iOS Software Engineer. And I'm really excited to show you some implementation of these great animations we've seen, and how you get your costumers really excited about this, and get them to buy your apps and download your apps.
A little bit about me, I love gardening, but you know, we're all software engineers, we're busy, right? We're at work at lot. I don't always get to get home and water my plants. So, I've made this demo app, which I call iPlant which helps me automate watering my plants even while I'm at work. And I will switch it so you can see it too. There you go. That's better.
So yeah, this is iPlant. The way it works is we've got this vegetable spinner here, so I can click around on it, and it's like my carrots or my sprouts. Click the middle drips button there, and my robotic gardener at home will water them. And on the back of it, we've got a simple webcam where I can see how, you know, the mint and stuff is growing there. Well, yeah, obviously, it's not animated.
Let's fix that. When we click on the spinner, on these different vegetables, if this method right here, selects or Set Selected Vegetable or Veg, that gets called. Wherein we Set the Selected Vegetable icon, which is this colorful icon right here to be hit, we set its Alpha to zero.
We then get the angle of the selected button on the spinner, and we set that to the spinner itself to rotate it. Finally, we set the Alpha of the selected vegetable to be 1 again to expose the newly selected vegetable. For this, you know, obviously, we're spinning something. We're doing a rotation already.
Wouldn't that be cool if we could make that animate. It's really simple. So, let's go and do that. So, what I'm going to do is I'm just going to insert this animations API with blocks, and the part that we want to have happen first here is hiding the selected vegetable.
We don't even need to animate that. We can just hide it right away. Well, like I said, the part that'll look really cool to animate is the rotation of the vegetable spinner. So, I'm just going to fix the indenting. I've moved that now within the animation block. So that's the part of the code that's actually going to get animated. We have completion as well, and that's where we want to put the exposing or the showing of the selected vegetable after everything is done. So, just that quickly, we get this cool effect where, you know, we're hiding it, we're getting a rotation.
You know, and if you did that on your own, you'd have to work our crazy things like nature qualifications to probably to rotate it, but you know, we're doing it all for free, so you might as well take advantage of it. It looks pretty cool. Next, let's get this watering procedure working right. You know, when water gets taken out of a tank, it doesn't just drop instantly, it moves smoothly, and this number also changing. It doesn't look so great, it just kind of blitz between, you know, 60 to 50 or whatever. So, let's make that animated.
When we click on the Drips button, it's this method right here which is startWateringProcedure that gets called. Here, we create a robo-gardener object which gets in touch with my robot at my apartment, and then we tell him to water the plants. Then we get the new water level from him, because he's given some water to the onions, in this case, and we want to set that to the volume label, which is this white label here, and then also set the frame of the water view, which is the blue view, which we can barely see to be the correct level as well. So, when I'm talking about animating, and also in the same, you know, breath talking about changing a text label, a little red flag should go up for you because changing text in a label really isn't something that's animated.
But we can find a way to get around that here. What we can do, in this case, how about we hide the label before we set the text 'cause otherwise, it is just going to blitz between them, which you know, doesn't really look too great. So, first and foremost, we'll just do some property changes before we get animated. We'll set the Alpha to 0 before we set the text. And then how about after we set the text, we show it again.
And now, we do want stuff to animate here, so I'm going to drop in another one of the blocks API with completion. We do want the volume label hiding this time to fade out. So, we'll drop that within our animation block. But when it completes, we still want everything else like the water level changing to animate as well. So, we'll put in just a simple animation block like that, and throw everything else within it.
Build and run. You know, that quickly, we're getting something really quite neat - we're fading out the text, changing it while the user can't see it, fading it back in after that's done, and lowering the level of the water. I think it looks pretty good, and it's pretty simple.
Something else to note, and if you look at blocks and you look at how they work with closures, it's cool because this robot object that I have here, it's autorelease, but the block-- or the blocks architecture is taking care of retaining a reference to it for me. So I don't have to worry about manually managing that memory like you might have with the older API. There's one last thing that we still would love to have animate, and that's this transition between the front and the back of the app. Let's do that too.
When we click on the Info button, the little I on the bottom, right, this method showBack gets called. Right now what we're doing is we're just removing the frontView and then throwing in the backView. You know, that's OK, but it doesn't look super good. So, we can use the new transitionFromView:toView :API. In this case, we want to go from the frontView to the backView. We could get rid of the stuff that was already there.
It's already been prepopulated just from where we're copying it from with some duration and the flipping from left, but it'll take care of now removing those from their super view and some other stuff that we used to have to do on our own. So just like that, we get that cool quasi-3D flipping over effect that you've seen everywhere. We still don't have it working on the way back, so let's do that right now.
When Done gets clicked or tapped, showFront gets called. And I just want to emphasize that when you're doing this on your own, do it symmetrically. If you're going to one side with transitionFromView:toView: you know use the same API on the way back. But here just to illustrate the transitionWithView, I'm going to be using that just to give you an idea of how it looks. So, on the way back what we can do is we just leave the code that we already had there for removing the views and adding in the new one. And instead, we'll pass the transitionWithView API the containerView which in this case the UIViewController's view.
And by doing that, we get the very same effect. And it's really this simple, there are lots of really hot things you can do with this new API. And this is all available for you guys to take a look at after we're done on the developer site. So thanks very much.
[ Applause ]
[André Boulé]
Thanks Tyler. Alright, what are we going to talk about next? As I talked about in the beginning of the talk one of the things that's really important to support in your app is support for different orientations. As you guys know in the first OS release, iPhone 1.0, many of our built-in apps only supported portrait. We heard very loudly from users that they wanted support for landscape. So, the same is probably true for your apps as well. How do we get that nice transition to and from portrait? We're going to talk about that now.
And one of the reasons you may want to do it, by the way, is because the keyboard is bigger in landscape, so it may be easier for some users to use it. But you should think about your UI. The screen has a different aspect ratio in portrait than it is in landscape, so it may make more sense for you to have a different UI in one orientation or another.
Let's look at an example. I picked this one at random, believe me. It's the YouTube app on iPad. And the goal when we created this app was to have a nice widescreen viewing experience in both orientations. So, in portrait, the viewport for the video covers the entire width of the display, and that leaves us room below the video for these big beautiful thumbnails for the related videos. When the user rotates to landscape, again, we have a nice widescreen viewing experience for the video, but now, we have some room for the thumbnails in a right bar on the side of the screen and the info below the screen.
So, during that animation, that bar on the right needs to slide in. How do we do that? Well, if you're using ViewControllers, your job is actually pretty easy. All you need to do is tell the system which orientation your app supports. You do this by overriding shouldAutorotateToInterfaceOrientation. The system will query you with different orientations, and you just say yes if you support it and no if you don't.
As a result of that, when the user rotates the device, the system will automatically manage a bunch of different things. The first is the rotation transform. Obviously, the top-level view of your ViewController needs to have a rotation applied such that the view is rotated and the user can read it.
The view frame of your view will be different in the new orientation. And this is again because in portrait the device is tall and narrow, and in landscape, it's wide and short. So, you'll get a new view frame. Both of these things will happen in an animated fashion because we don't want the view to just pop to the new rotation and size, we want that to animate. So, this system creates an animation for this.
The Status bar also needs to follow your app's orientation. So, the system will change the Status bar. And finally, again, when you're creating animations especially rotation ones it's probably the right thing to turn off user interaction during the rotation animation itself. So, the system does that too. What do you have to do? Well your job is to layout your UI for the appropriate way for the orientation. In this example, I have a UIView subclass from my ViewController's view, and I've overridden layout subviews.
In my layout subviews, I checked for the interface orientation, and if it's landscape, I do one particular layout, and if it's portrait, I do my portrait layout. Now the beautiful thing is that this will actually be called within that window rotation animation. What's the result? Well, if you change some frames or set some Alphas or things like that, any animatable property changes that you make within the layout subviews they will animate automatically, you don't even have to create the animation. The system just does it for you.
That's great. But of course, in the YouTube example, you know, I have that bar on the right. It's only there in landscape, it's not there in portrait. And so, certainly, layout subviews is not an appropriate place to be making changes to your view hierarchy, you know, adding views or moving views. You definitely don't want to do that in layout subviews. So what do we do? Well there's a solution.
You have some methods in your ViewController that you can override, and they will be called before, during, and after the animation. So, the first one is willRotate, and this will be called on your ViewController, and when the user has changed the device orientation, it's called just before that animation starts.
So, willRotate is the perfect place to be putting in views that will become visible in the new orientation. So, if I'm rotating from portrait to landscape in Youtube, that's when that right bar would get added to the view hierarchy. willRotate will be followed by a call to willAnimateRotation.
This is just before the animation, but again, the system has created an animation for us and willAnimateRotation is called within that animation, which means that any animatable properties that you change within willAnimateRotation will be animated. So, if you make some frame changes in here or Alpha changes and whatnot, those will be animated.
Now a special note, if you do make frame changes and you want those to trickle down to all of the layout subviews of the subviews, make those frame changes and then at the very end, call layout if needed. And then all your layout subviews methods will be called as well, and again, within that animation. So, they will be animated as well.
Finally at the end of the rotation animation, you'll get a call to didRotate. Perfect place to do some post animation cleanup. If I'm rotating in YouTube from a landscape to portrait, well after the animation is complete, there's no more bar on the right side of the screen, so I can go ahead and remove that view from the view hierarchy.
These are the methods, the full method names. But notice I've highlighted in yellow the parts for you to remember: willRotate, willAnimateRotation, and didRotate. So, if you are using ViewController, that's really all it takes to get a good window rotations in your app. Now, some of you may not be using ViewControllers. How do you get rotation in your apps? Well, you need to listen for device orientation changed notifications.
And in response to that, there is a bunch of work that you need to do. You need to change your rotation transform, your view frame, the animation for both those things, the Status bar, disabling of User Interaction, does that look familiar at all? I mean all of the things that we just talked about that ViewController does for you, now, because you are not using ViewControllers, you have to take care of that yourself.
So, what's the message here? If all you use ViewControllers for is to get the rotation support, it's worth it just for that. So, it's, you know, very easy to go in and just put in a ViewController in your app that doesn't have one. And it will make your life a whole lot simpler for rotations. So, I encourage you to do that. Let's talk about performance.
The iPhone is truly magical with, you know, when you are interacting with the thing, it looks like magic because everything is just so smooth, it feels like a physical thing. That illusion is completely ruined if your animations are choppy. So, it's critically important to get great performance as you know. So, my first message for you here is it's absolutely important to test on real devices. This simulator is great.
I use it for almost all of my application development, but when it comes time to measuring performance, it's just not appropriate to use. Its performance characteristics are completely different than that of an iPhone, iPod touch or iPad. The different hardware generations have different performance characteristics as well. So, I encourage you to test on multiple devices of each of the different generations. You can use instruments to get a precise measurement of your performance.
Many times, performance is so bad that it's extremely visible both to you and to your users. But once you've optimized it, and you're up to, you know, 40, 50, 55 frames per second, it gets pretty tough to tell if the change that you've made has made performance worse or better. But instruments can measure it very precisely. So, you can use that and see if you're making performance better or worse.
So, how do we make performance better? A few of these go without saying, but I'm going to say them anyway. During your animation is not the appropriate time to calculate Pi to a million digits. Save that for after your animation, please, if you want your animations to be smooth.
If you have views in your app that are not visible to the user, there's a cost to those views. So, if you can remove them from the view hierarchy, they're not providing any benefit otherwise, right. Take non-visible views out of the view hierarchy during your animations. It can definitely improve your animation performance. And finally, don't try to draw every frame of the animation using drawRect.
You're not taking advantage of the hardware acceleration behind UIView and CoreAnimations when you do that. So, use the APIs that we provide you to get your animations. View hierarchy depth is another critical thing to consider when you're measuring the performance of your app. Let's look at an example. This example is called iFruit. And this is one table cell from my iFruit app. What does it do? Well, it displays an image of my fruit, the name of it, and a bunch of classification information.
It looks harmless enough, right? In reality, of course, this isn't what my app looks like. My app looks more like this. There's a bunch of these table cells. Well, what's going on here? I mean when you look at this, each one of those things in a dotted outline is a view. That's a lot of views. I have about 10 views for each table cell.
So, that's a lot of compositing that the system needs to do to animate this table when you're animating the scroller for instance. So, if you can reduce the depth of your view hierarchy, it will absolutely make the scrolling faster in this app, a whole lot faster. What could you do? Well, in this case, they're just static labels.
I've used UI labels in this case and UIImageView. If instead of doing that, I simply draw in the super view which is the main contents view of this cell, I could reduce this example to one or two with a separator, but almost of one view per cell. So, it would have a dramatic impact just by converting from using UILabel to a drawRect. It's less for the system to composite if it's only one view.
And in many cases, it'll use less memory too. So, I'm not telling you not to use UILabel. UILabel is really handy in a lot of situations, but I want you to understand that there is a cost to every view that you put in your app. So, if you can reduce the number of views, your performance will improve.
Now, of course, you can't always use a drawRect solution. I just finished telling you that you shouldn't draw every frame of your animation. Let the system animate things right? What if I want to animate the position of these subelements within the cell? I can't use drawRect in that case.
If I had UIControls, some buttons or sliders or whatnot in the cell, also drawRect is not going to work there. If you have an otherwise complex view hierarchy that's just not practical to draw, you can't use a drawRect solution. So, what do you do? Well, I'm going to talk about a solution that you can use today called shouldRasterize.
What shouldRasterize does is render an entire view hierarchy, a view and all of its subviews. It renders that off-screen and caches the result and uses that cached result. Now, I have to warn you that shouldRasterize is like a very sharp knife. Sharp knives are great for chopping up fruit, but you can cut yourself very easily. shouldRasterize can hurt performance just as easily as it can improve it.
But it's important to talk about it here because it can have dramatic results. That's it. If you have animated subviews and that the superview of those subviews has shouldRasterize, your performance will suffer because you're rendering off-screen and caching this thing that's no longer going to be valid when the subviews animate.
There's a limited cache size that's available for shouldRasterize. So, if you go and turn on shouldRasterize on every view in your app, your performance will get worse. That's it. If you turn it on in those places where it's appropriate and only those places, and only for the time during at which it's appropriate, you can turn this on and off as you want. If you turn it on in just those right places, it will dramatically improve your performance.
So, in my iFruit example that I just showed you, when the user is scrolling around on the tableView, I would turn on shouldRasterize on the cell itself. And then when I want to animate the subviews, then I would turn it off. Here's how you do it. Use it as little as you can. I've warned you. _myView.layer.shouldRasterize = YES.
OK. A couple of performance pitfalls, things that are very expensive for the system to do. Any views that are non-opaque, transparent views are very expensive in our systems. Any layers that require off-screen rendering are very expensive. Now shouldRasterize layers off-screen, right? Yeah, but it caches, so it's a special case. Anything that renders off-screen and doesn't cache is very expensive.
Now, what is this exactly? I'm not going to go into detail here because we don't have the time. But there are some performance sessions in advanced CoreAnimation sessions that will describe to you exactly what these two things are. But what I want to highlight very quickly is in instruments when you run with performance tool CoreAnimation. Well, as you saw, it gives me a nice big 60 frames per second.
Nice. And in the bottom left, there's this box in which you can turn on different options. Let's blow that up. You can turn on color-blended layers, and color off-screen rendered views. And those things that flash when you turn that on, those are the things that are really expensive and that will hurt your performance, and so you want to try to minimize those.
If you want to know more about these two things, you can go to the Advanced Performance Optimizations on iPhone talk or the CoreAnimation in Practice talk Part 2. Part 2 is given by one of the CoreAnimation gurus who really knows the stuff, and he's going to explain in detail how it works, how it's implemented, and why it's expensive. So, I would encourage you to go to those talks.
Alright. Let's talk about CoreAnimation. Again, one question we've gotten from you guys is when should I use UIViewAnimations? When should I use CoreAnimation? It's interesting. I mean they're kind of separate APIs, and they both have their pros and cons, right? When should you use each of them? Well, here's my advice to you.
Use UIViewAnimations whenever possible. The API is consistent with the rest of UIKit and designed to work with the rest of UIKit. So, the vast majority of times, you'll want to use UIViewAnimations. That said, CoreAnimation provides you with more power. If there's something that you can't do with UIViewAnimations, it's possible that CoreAnimation does enable you to do what you want to do. So, in those instances, you would dig down into CoreAnimation and use it directly.
Now, we talked about animatable properties. Well, the ones that I showed were the frame, the Alpha, the transform. CoreAnimation exposes even more animatable properties that you can use. So, if you need to use these, you could go directly to CoreAnimation. We'll see an example of how to create an animation with CoreAnimation.
I want to talk very briefly about the different types of animations within CoreAnimation. CoreAnimation works a little bit differently than UIKit. There's this thing called implicit animations. When you want to animate something in UIKit, you create an animation, change your properties, and then commit the animations. If you don't have a layer delegate in CoreAnimation, anytime you make a change to an animatable property, it will actually animate for you automatically without having the need to create an explicit animation. So, it's something to keep in mind.
You can also create explicit animations using CABasicAnimation. That looks a little more similar to what we do in UIKit. So, I'm going to show you the same example opacity change or the Alpha dissolve on my view as I did in the beginning of this session, except this time, we're going to create that animation using CoreAnimation directly. So, it looks similar to what we did in UIView actually.
I've created an animation using CABasicAnimation, and I'm operating on the opacity property. That's CoreAnimation's word for Alpha in UIKit. I've set the toValue what I want to animate to to one, duration 3 seconds, timing function that's kind of like curves in UIKit. You know, I've set it to ease in, ease out to get that acceleration and deceleration. And finally, I add the animation to my views layer myView.layer addAnimation.
That's how you get to the CoreAnimation layer from your view. What's the result? It looks exactly like it did in UIKit with one notable exception, the view disappeared after my animation. Well, why is that? CoreAnimation, when you create animations, you're only affecting that layer for the duration of the animation, you're not changing the property itself permanently.
So, if you don't want your layer to disappear at the end of the animation, you would just add this one line and explicitly set it to 1. And then not only will it animate like you expect, but at the end of the animation, it will stay visible. So that's a key difference between UIViewAnimations and CoreAnimation that I wanted to illustrate. So, what are some of those advanced things that CoreAnimation enables you to do that you can't do directly with UIView? Well, motion along a path is one of them.
When we animate with UIView and we change the position of something, it animates directly in a straight line from the From to the To. CoreAnimation allows us to pass in an arbitrary CGPath to animate along that path. It's-- the easiest way to show this is with an example. So, let's invite Tyler back up for another demo.
[ Applause ]
[Tyler Hawkins]
OK. This isn't quite as coherent of a demo, it's not a plant thing, it's just a picture-viewing app that we've made, simple enough. We've got our picture of a cute dog; we've got our trash can. And the idea of what we want to be able to do here is dispose of a photo.
So, if you've just been here for the first half of the talk and you kind of just had UIKit on your mind, you probably think that that was the way to go about animating this, kind of zooming the picture down into the trash can. Let's try doing that with UIKit and see how it looks.
So, with the UIViewAnimation, what we can do is just like everything we've seen before, set the center of it, we'll create an animation, set the center of it to the middle of the garbage can, set the transform to shrink it down, and set its Alpha to fade it out a bit. And this thumbnailPressed method gets called when we click on the image, so we can make that happen. And, you know, that's alright.
The dog zooms down to the garbage can, he shrinks. But something that's kind of funny is, you know, when you throw something into the garbage can, it doesn't fly magically at it unless you're throwing it really, really fast, and it also doesn't pass through the side of the garbage can like we saw happen there. Now, we want something a bit more realistic, kind of an arc, as the picture gets thrown into the trash. We can do better. Let's try doing that with CA, CoreAnimation. So, I'll just erase everything that we have there.
And we know that path. Like I said, I've got this idea of kind of an arc that I want the path to follow. Let's draw that 'cause it's the same type of thing when we want it to follow the path. So, I'll create a CGPath and create it from the picture to the trash, have a little bit of control point so we get a nice quadratic curve, and the view above the setup to draw this path. So, when we click on the dog this time, you get the path.
That looks pretty good. You know, maybe that's not perfect gravity kind of drop-off, but you know, that's fine. That's what we want to do in this case. You don't need to be drawing that path, so I'll overwrite that with our CAKeyFrameAnimation. The property that we're changing is the position right there, and we'll actually just set the path to be the CGPath that we've created and made above.
We'll then set the duration and then add that animation to the layer of the thumbnail or the picture of the dog in this case. Alright. That's kind of it, isn't it? Something's kind of wrong using that still. We want it to shrink. So, let's do some more work to make it shrink and fade out like we saw in the kind of the naive approach. So, first, another CABasicAnimation. As Andre said, CABasicAnimations work by having sort of a From and a To property. Here, we're modifying the transform property.
So, we can assume that the From is going to be the identity transform, sort of the normal size, and we're going to be shrinking it or passing it to a To-value of a CA transformed 3D scale of a tenth of the initial size. We then also want to fade it out, so we have another basic animation where we set the To-value to be 50 percent or 0.5.
Finally, to make all three of these happen together, we're going to need to use a CA Animation group wherein we pass an array of these three animations that we've created above and then we have a tiny function we do in this case just to make it look a little better. The same sort of this-- the CA version of the ease in and out that we saw before in UIKit, and we set the duration. And then finally, just like before, we add it to the layer of the thumbnail.
So, just like that, we get this cool kind of swooping down effect, have the image fade out and shrink down. We could do a bit more work to make the image disappear, but I think this gets the point across for now. It's very similar to this that you could do really cool things like this quasi 3D-like animations that you see in like CoverFlow and CA has a lot of power if you need it. It's really great. Thanks again.
[ Applause ]
[André Boulé]
Thank you, Tyler. Alright, so you saw the CA group animation there. We didn't talk about that. And that's because there's two more sessions on CoreAnimation. CoreAnimation in Practice, Part 1 and Part 2. Part 2 is that really advanced session I encourage you to go to, and they are later today.
So, what did we see today? We saw UIView Animations. New in iOS 4 is a blocks-based API for UIView Animations. I encourage you to go try that out. And I encourage you to dig deeper and get your hands in CoreAnimation directly if you need to. For more information, you can contact Bill Dudney our Application Frameworks Evangelist.