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-362
$eventId
ID of event: wwdc2008
$eventContentId
ID of session without event part: 362
$eventShortId
Shortened ID of event: wwdc08
$year
Year of session: 2008
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC08 • Session 362

Controls, Views, and Animation on iPhone

Essentials • 49:46

The UIKit framework in iPhone OS provides a core set of standard user interface controls, such as buttons and sliders. Discover how to use these control objects to respond to user gestures, such as taps and drags. Learn how to use the view hierarchy to best effect and create your own views when you need to extend the user interface.

Speaker: Dan Keen

Unlisted on Apple Developer site

Transcript

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

Good morning. My name is Dan Keen. I am an iPhone software engineer, and I'd like to start out by actually showing you a demo of what you're going to be able to do when you have finished here. So if we could go to the wolf, please. So the product that I'm actually trying to show you here is-- it's called Restaurant Viewer. It's a sample code that you'll be able to download. And it's pretty straightforward. Here we have Lebec Fin. It's a restaurant in Philadelphia.

And the first thing I'm going to show you is that this app supports rotation natively. I've slowed down the rotation animation on this phone. So you'll find that when I rotate it-- when I rotate it-- that the stars are moving to a different location underneath the title, the descriptions moving over to the right, and everything is animating very nicely.

If we rotate it back, you'll see that again, everything animates very nicely. In addition, there's a nice Categorize button here. If we click it, a sheet is going to pop up, and Lebec Fin happens to be a French restaurant, so let's categorize it as French. So you touch French, and the restaurant name animates there very nicely and disappears. So at the end of this talk, I'm going to actually walk you through setting up all the code to do that. So if we go back to slides.

So as you know, we're here to talk about three things today. Controls-- no, hang on. hitting the backwards button. Awesome. We're here to talk about controls, views, and animation. So the first thing is controls. What are controls? Well, we're going to go through some sample controls to use, and how exactly to use these controls.

We're also going to talk about views. We're going to say how do views actually work, and we're going to give you some examples of nesting and translation. You'll see here that the control that's above is actually a combination of several sub views. And finally, we'll talk about animation.

So here we're going to talk about some flexible, fast techniques to use for animation to give your apps impressive, yet useful bling. So here I just want to say, well, what does that mean exactly? Useful bling? Bling isn't useful. It's just pretty. Here we have an example movie where you're clicking on the Groups button, and everything's animating out to the right. If we actually were to look at this closely, the All Contacts is moving out to the right and fading.

The Plus button actually doesn't move. It just fades. And the Groups button is actually moving over and cross fading into the Groups title. So those three different animations are actually providing a lot of context to your user, and giving them an idea of what's exactly happening. It keeps them focused on the content itself. It's not really superfluous.

But before we get into controls or animations, we first need to talk about views. Some of this might be a review for some people that have attended other sessions, but let's still go through it. So what is a view? Well, a UI view inherits from NSObject, which is our base class.

From there, inherits from UI Responder. UI Responder actually deals with handling the event forwarding chains. And then from there, we get UIView. OK, so a UIView is a UI responder, which is an NSObject. Awesome. What does that actually mean? Well, a UI view, before we actually talk about the UI views and how they handle the drawing contents, we first need to say, where is it actually going to draw the content on the screen? So most of you are probably familiar with the coordinate system. In our case, the coordinate origin is up in the top left of the device. If you're familiar with AppKit, it's on the bottom left. So this is going to be a difference for you if you're used to AppKit.

All right, so now we have a coordinate system. So how do we actually lay out our views? So what is a view, again? A view is just a rectangle in space. It occupies a quadrilateral region. That's pretty straightforward. How do you define this region in space? Well, the way we do it is we use a CGRect struct. A CGRect struct has an origin and a size.

So in this example, the origin of this view is at 20, 0. So it's over 20 units and down 0. And the size of this rectangle is actually 200 in width and 100 in height. So the second parameter to a CGRect is not actually where the lower point is. It's the size offset from the origin. It's just a nice thing to keep in mind.

All right, so that's some basic UI view stuff. Pretty straightforward where you are going to put your views, how you do with that stuff. What about a control? Well, we saw this inheritance chain already. How does control fit into here? A control actually just inherits from UIView. So what does control give you? I mean, it's just a view, right? Well, actually, a control already sets up a bunch of the handling of events for you. For instance, let's say that you wanted to handle a button up press in your control.

There's a way to set the target for an action. So we have this Add Target action for Control Events call. In this example, we have a Dismiss button in the top left here, and we're going to do the action on the target down here. So the way you're going to do this with the Add Target action for control events is the control event that we care about here is a UI touch up inside. This is your equivalent to a mouse up inside that you might be more familiar with other things like AppKit. So we pass this for control events.

We set the action to be dismiss on the target, because that's actually what we're going to be doing. And then we call add target action for control events. That's all you have to do. And then when you actually touch up inside, the UI control will automatically send the dismiss call to your target. So this is very easy to use.

Now, if you're more familiar with things like mouse down, mouse up, mouse dragged, stuff like that, there's actually a pretty easy correlation between that and what we're doing here. You replace the word mouse with touch, and you're pretty much done. UI mouse up inside would become UI touch up inside. Some commonly used UI control events that you might care about. UI control event touch up inside, as we were just saying. That's equivalent to a mouse up inside the button.

UI control event touch down. That would be your initial mouse down in case you wanted to change your state. And if you were really fancy and you were supporting multi-touch, there's also UI control event touch down repeat. And I want to point out that UI control events is a bit mask. So if you wanted to do the same action on multiple control events, all you have to do is bit or these values together, and you do it.

So that's how to use controls. How about some example controls? Well, here I have three very commonly used controls. The first one is UIButton. In this example, it's a UIButton of the style rounded rect. This is all provided for you. The second one is UISegmentedControl. And the third one is UISlider.

The nice things about things like segmented control and slider is that all the logic about how to highlight certain segments, how to respond to sliders, callbacks for you, and all that stuff is already done for you. So all you have to do is actually implement one of those, and you get the proper callbacks for what you're trying to do.

Now, this is just three examples. There are a lot more. I highly recommend you download the UI Catalog example from developer.apple.com/iphone. It will show you all of-- well, maybe not all, but pretty much all of the controls that are built into UIKit. And it'll also, because they're all there, it'll show you how to use them. The sample code shows you how to wire them up, et cetera.

All right, so now we've talked about controls. But before, we were talking about views. And you saw in my first overview slide that we had these subviews within Views. And they expanded out nicely for that navigation bar that we were showing you. Well, how does that work? So views can contain subviews. Here we have that navigation bar again split out.

And we'll see that they all go back into the subview. Well, how does this work? When you add a view as a subview of another view, let's say you have this coordinate system, right? And your origin is here, and then the view that you're adding to is up here. What happens when that view's origin moves? You don't want to be paying attention to that as a subview, right? So what we do is the coordinate system translates as you add into subviews.

So when you call a frame, which you might have seen in other presentations, what does that actually mean? So frame gives you your coordinate system in the superview's coordinates. So in this case, the all contacts would be offset by 40, 50 pixels here in the x direction. So that would be the origin of the frame for that.

Bounds is a different call. Bounds tries-- it tries to give you your frame in your coordinate system. So in that case, if you call bounds on all contacts, the origin will actually be at 0, 0. Now, there are some special cases where bounds is not at 0, 0 in the origin. For instance, you see why scroll view.

That's a very special case. For the most part, you will not have to worry about that. So you can assume that bounds is 0, 0. Bounds is very nice to be used when you're laying out your subviews, because frame is-- is superview coordinates, right? So if you get your own bounds, that's corresponding to what this subviews frame relates to, right? So if you get your bounds, then you lay out the subviews within those bounds, everything works really nicely. And we'll see that in later demos.

All right, so I've been talking about controls and views, coordinate systems, all this awesome stuff. It's pretty boring, right? You've fallen asleep. Let's talk about animations instead. So how do we do animations on the iPhone? We have a UIView animation category on UIView. This is how we do almost all the animations that you've seen on the iPhone. There are no animators. There's no callback-based animation on the iPhone. We're not going to call you back every single frame to say, all right, where is your position now? Because of this, the animations are very fast. You're not having to recalculate every time.

We're not doing this because we actually use core animation for all of the animations that you're going to be doing using the UIView animation category. It's a transparent usage of it for you. As we all know, core animation is very fast. Why is core animation fast? That's because core animation uses the hardware render. So it's automatically hardware accelerated for you. You don't have to think about, is this going to be able to be accelerated or not? If you're using UIView animations, it is. You're done.

All right, so how do we actually do this? The first thing I'm going to note is that UI view animations are asynchronous. You set up a delegate and callback for when the animation is done. But while the animation is happening, you actually-- it's not a callback based animation. You're not getting callbacks every frame, so you don't really have control during that time. Unfortunately, your app does have control. As soon as you finish the animation, control is going to return back to the main thread of your app.

So basically, you're going to set up animation. Then once you've done that, you're going to commit the animations to be rendered. That information is going to then be off-laid to the hardware renderer. And during the animation time, the hardware renderer is going to be taking care of that for you. And then when it's done, it's going to give you your callback.

All right, easy enough. However, during this block of time here, your app still has control, right? You can still do whatever you want. In fact, you might decide that you want to calculate what the thousandth prime is, because that's going to be what your user actually cares about next.

If you do that, you are going to slow down the animation. The animation does run in the hardware renderer, but there's shared resources. There's a shared memory buffer. It's going to be asking to pass information over to it, et cetera. So even though Flow is going to return to your program, don't do stuff here. This is how we try to make our animations as fast as possible. Just consider it dead time. You're not supposed to be doing anything during that time.

So how about some common things that you might want to do with a UIView animation? All the animations by default have a certain duration. If you want to change that duration, for instance, if you want to slow down the rotation animation, you call setAnimationDuration with the time interval.

Let's say you wanted to change the curve of the animation. By default, these curves are ease in, ease out. What this means is that they accelerate up to speed, and then when they get to the end of the curve, they decelerate. If you wanted to set this to ease in only, or ease out only, or even just linear animation, you do that by calling setAnimationCurve.

Finally, as we've all said, there are these callbacks at the end. You can change what the callback is, who gets called back. So you do that by calling setAnimationDelegate and setAnimationDidStopSelector. You might want to do this if you have different selectors for different animation finishing. All right, so that's all pretty straightforward, right? How do we actually use this stuff? So a little slide demo here. Let's say we just wanted to fade a view out. So the first thing we need to do is we need to create a UIView animation.

So we do that by calling UIView begin animations, my animation, context nil. All right, so there are two parameters here. The first one is my animation. The second one is context. What do these actually mean? The first one names your animation so that when the animation is finished and you get your did stop selector callback, it actually passes back the name of the animation to you.

And you can use this to decide, OK, which animation actually finished, because you could have multiple animations going on at once. As we showed in the navigation bar in the beginning, when you clicked on the Groups button, there are actually three animations there. And each one of them is probably named a little bit differently so that you can respond to its did finish differently.

The context is what gets passed back to your desktop selector as well in case you need to use it for anything, such as removing buttons or whatever. All right, so now we're actually going to fade out the view itself. How do we do this? Well, we get our view and we set its alpha to zero. So we already have the view, you know, this is slide code, my view, and we call setAlpha to zero.

That's it. We're done for that. And then we have to commit the animation to the renderer. And so we call UIViewCommitAnimations. That's it. That's all you have to do to fade out a view. This is very, very easy to do. Now, what I want to point out is that we set this alpha property on the view, right? You can set as many properties as you want. You can set the frame. You can set the bounds. You can set all kinds of things.

What I want to point out is that this is a stacked animation. So if we call setAlpha within this block, and then we call setAlpha again, let's say we call setAlpha 0.5 first and then 0 second, you might all expect it to go from where it currently is at 1 to 0. But that's not actually what happens.

Because it's a stacked animation, the animation takes the previous value and then just animates to the last value. So in that case, at the beginning of the animation, it's going to jump immediately to 0.5, and then it will animate nicely to 0. This is a side effect you might not expect. It can actually be used to your advantage in certain circumstances. The easiest takeaway from this is only set a property once. Otherwise, you're going to end up confusing yourself. So with that, I'd like to take you to a demo. So if we go to the laptop.

All right, so basically what I was just showing you with that slide code, we're going to do the same thing again. But I'm going to actually set up a few views for you as well, and a control, so that we can go through all that stuff. So here, this is called Simple Animation. This is also available as a download from the sample site.

This actually is not going to be exactly the same as what you'll see on the sample site. On the sample site, we set up all the UI through Interface Builder, because that's much more common. That's what you're probably going to be doing. But here, for the example of this talk, we're actually going to be doing it all manually ourselves.

So the first thing we're going to be doing is application did finish launching. We're just going to be doing everything in the delegate. Again, this is a sample code. This is not how a real app would behave. What we're going to do is we're going to create a rectangle in the middle of our view. So we get the bounds of the window.

As I was saying before, the bounds is the coordinate system in your own coordinates. And then we're going to set up a rectangle within these bounds. So we set the width of the rectangle to rectangle width and the height to rectangle height. And then we're actually going to center this rectangle within the bounds. So we do that by subtracting from the width of the bounds, the rectangle's width, and dividing by 2. And the same with the height.

Pretty easy. So now we have where the frame of the rectangle is going to be. The next thing we do is we create a UI view to be this rectangle. And we're going to make it blue because it's pretty. So we init with frame rectangle frame. We set the background color of this rectangle to be UI color blue color. And then we add that rectangle to our subview.

So what happens when we run this now? As expected, we get a nice rectangle centered in the middle of the frame, and it's blue. Pretty straightforward. We also are going to want to have a button so we can do something with this rectangle. So the first thing we need to do is we need to create this buttons frame. So we're going to base that on the rectangles frame. We want the button to look similar to the rectangle, but we're going to move it down a bit. And we're going to change the height of the button a bit.

We then create the button itself. So now we're calling UIButtonButtonWithType UIButtonTypeRoundedRect. This is the same button type as I showed you on this slide a while ago. We're going to set the frame of the button to the button frame. We're going to set its title to dismiss for the control state normal. So as normal, it'll be dismissed. And we're also going to add that to our subview.

So now when we run this, as expected, we now have a button here. This button behaves nicely. It highlights. If you were to drag your finger, it'll unhighlight, because you're far enough away that they expect it not to be a touch up on the button. But it doesn't do anything. Well, how do we fix that? Well, we were talking earlier about how we call the add target action for control events. So we do that for this button here. We're going to call addTargetSelf with the action change for the control events, UI control event, touch up inside.

And then we need to have, obviously, that change. OK, well, that's not really doing anything right now. What do we want this to do? Well, if we were to look at the demo, the button says Dismiss, right? So we probably want to dismiss that rectangle when we get the change call.

So the first thing we do is we figure out where are we right now when this button got clicked. This way you don't have to keep state, you can just look up state when you need to. So where is the rectangle right now? What's its frame? And what are the window bounds? What's, you know, my canvas, what am I laying this out in? And then we need to figure out are we sliding this rectangle off screen or are we bringing it back on screen.

Easy way to do that is to look at the origin of the rectangle's frame. If it's outside of the bounds of the window, then it's already off screen, right? Because it's all the way over there. So we're sliding out if it's still on screen, otherwise we'll be sliding in.

So now we have our state. We know what we're going to be doing. How do we actually handle it from here? Well, the first thing that we want to do is we want to call UI application shared application beginIgnoringInteractionEvents. Why do we do this? We're going to be doing an animation. This animation is going to take time. During that time, as we said before, control returns back to your application. OK? So control returns to your application. You've started this animation. It's running. The user goes ahead and clicks a button.

Were you expecting a button press at this time? Probably not. You were probably changing state of your application in some way. And so a button press now has perhaps unforeseen effects. So during this time, it's best to just ignore interaction events. This means any taps that the user does will not translate to your application.

All right, so we're now ignoring interaction events. So now we set up the animation itself. So we call UIView BeginAnimations. We're going to be passing a nil context here, because we're not actually going to be using it. But we are going to pass two different names for the animation, depending on whether we're sliding out or sliding back in.

These names are all defined up here with these nice defines. We're going to set the duration to one second, again, defined up above. And then we're actually going to change the animation curve here. If we're sliding out, we want to say ease in. And if we're sliding in, we want to say ease out. Why do we do this? Well, this view is going completely off screen. So if we say ease in, then we're going to watch the view accelerate off screen and seem like it's going off screen very quickly.

And if we say ease out, it's going to gradually slow to a stop. If we were to leave the default curve, it would seem to accelerate off screen. And then as it got off screen, it would slow down a little bit. You can also change this by changing even where it goes off screen so that when it gets off screen, it's only halfway through your animation. But it kind of makes more sense to just change the animation curve itself. And then finally, we want to set the animations delegate to ourselves so that we know when we're done.

Then we need to actually do something, right? We've been setting up this animation. Now we actually need to do something with the animation. If we're sliding out, we can just set the rectangle's origin to be the width of our bounds. That means it will be completely off screen.

If we're sliding back in, we're going to do the same thing that we did up here to calculate the origin of the X so that it will return to be exactly centered on the screen. So now we calculated the new frame. We need to set the frame on the rectangle, so we're setting that property. In addition, we're going to fade out the view. So we call setAlphaSlideOut. Whether we're sliding out, we call it 0 or 1. So that as it slides out, it fades out. As it slides in, it fades in. We then need to commit the animation.

[Transcript missing]

It automatically slides out. Click the button again. Oh, we can't click the button again. Hmm, right. See, up here we began ignoring interaction events, but we never stopped ignoring interaction events. So now the app does not respond to you at all. Not ideal. So what we need to do is we need to resume the interaction.

So here we had set our delegate to ourself. When the animation finishes, it will call by default, the animationDidStopedFinished context selector. You can change this yourself by setting what the selector is going to be that's called. In this case, we didn't. When that happens, we want to call endIgnoringInteractionEvents. All right. So if we do that-- You dismiss, leave return, everything behaves nicely. If you were to tap like crazy, nothing behaves badly. So that's a nice, simple demo of how to use animations and controls.

Can you go back to slides? Thanks. So that's pretty simple animations. We're moving a view, we're fading the view, we're responding to controls, pretty straightforward. What if you wanted to do fancier animations? For instance, in the demo that I showed you originally, we supported rotation. That's really cool. That's one of the nice features of the phone, is this nice inherent rotation support.

How do we do this? So that demo actually uses a UIViewController. UIViewControllers are the way to create your app on the iPhone. There is so much support built into UIViewControllers that you're going to want to use. There's a category on UIViewController called UIViewRotationCategory. How does this work? Well, the first thing you have to do is you need to tell that you support rotation.

So when the device gets rotated, your controller will actually be called shouldAutoRotateToInterface Orientation. By default, this only returns yes for portrait mode. If you want to support other orientations in your controller, you need to return yes here. Now, there are three parts to this actual animation. There's a header view, a footer view, and the content view.

What do you mean? All I have is my app, right? What's a header view, a footer view, and a content view? This is actually fairly common in our apps. You'll see this all the time. We have a header view, like a nav bar, and a footer view here, like in Mail, there's the buttons at the bottom, or in iPod, there's the tabs at the bottom, for instance.

So how does this actually play into the animation? Oftentimes, these are going to be changing their complete styles when you go to a different landscape. For instance, the bar at the bottom has a much different look when you're in landscape mode. It becomes shorter, the buttons actually change their types and all that stuff.

So actually animating that would be pretty hard and cumbersome, and it might not even look right. You might want to have a completely different layout in that case. So what happens when the animation happens is that the footer and the header are going to slide off screen while your content rotates. And then when the content has rotated 45 degrees, they're going to start sliding back on screen in their new layout configurations, so that they can change their look when they're completely off screen. This provides a very seamless experience. So we'll see somewhat of a demo here.

So you can see that they're going to be changing while they're completely off screen. Now because of this, the animation actually happens in two different stages. The first is the first 45 degrees when they're sliding off. The second is the next 45 degrees when they're sliding back on.

Because of this, there are actually two different calls that you get. Once you say that, yes, I support this interface orientation, please rotate to it, you will animate first half of rotation to interface orientation and the duration of this rotation. And then you will get, will animate second half of rotation from interface orientation. Note that the parameters here are slightly different.

In the first one, it's saying where you're going to be animating to, because by default, your UI is currently in this current interface orientation. When the second one gets called, the interface orientation for this application has already been changed. So it tells you where you were coming from.

Both of them provide durations so that you can do whatever you'd like. You know how long this animation is going to take. So we need to do our animation in two steps. Each step will animate our subviews halfway. So in the demo earlier, when, for instance, the stars were moving from the top right down into the bottom left, they need to only move halfway each time this callback happens.

Okay, so that's a little complex. It's kind of hard to describe on slides. Luckily, we'll walk through it in a bit. But before we do that, I want to talk a bit about lower level animations. So UI view animations give you this capability to change inherent properties on the view, like its frame, its bounds, its alpha, you know, pretty, pretty -- there's a fair number of properties that you can use there.

But what if you wanted to do fancier animations? What if you wanted to do-- you wanted to move this view along a path. You don't want to just say, well, it goes from here to here. You want to say, well, it actually goes like this and back. How do you do that? Well, for that, you're going to want to use core animation.

So lower level animations are possible with core animation. And this works incredibly well with UI views. For the most part, you can just say, all right, I'm a UI view. Let's do a core animation. How do you do this? Well, let's say that we have this path that we want to move the view along, and we have a view already. The first thing we need to do is we need to set up a keyframe animation by calling ca_keyframe_animation_animation.

We then want to say that this animation is changing the position property. We want to set its path to this CGPathRef that we were already given. We want to set the delegate to ourself, because it's good to set the delegate to ourself. That way, we always get the callbacks we expect.

Then we want to set the Fill Mode to Frozen and Removed on Completion to No. And the reason we do this is so that the animation remains after it has completed. And then we say, "View Layer Add Animation for Key." So Layer gets the CA Layer Banking Store for the view.

We say, "Add Animation for Key," and you're done. That's it. Your animation will automatically happen at the end of the run loop. So that's pretty easy to do. Now I want to actually show you how to do that and also support rotation in your apps. So if we could go back to the demo machine.

So now we're going to be looking at the Restaurant Viewer app that I showed you in the beginning. A bunch of the code is already written here. It would take too much time to go through all of it. Luckily, the Restaurant Viewer is also available for download. It's associated with this session.

So if you go to your attendee site, you will easily find it. So the first thing we need to do when the app finishes launching, because this is now a UI view controller app, is we need to actually set up the view controller. So we do this, and application did finish launching in our delegate.

So what we're going to do is we're going to create a new restaurant, new demo restaurant. Ideally, you would have a much more complex backing model. In our case, we don't need it. We then set the ViewController's restaurant to be that restaurant. We're going to release the restaurant, and we're going to add the ViewController to the restaurant. I'm sorry, the ViewController's view to our window.

So when we do that-- does this actually work? Nothing, right? OK. We've created a view controller. We've added its view to our view. But the view controller doesn't actually do anything. So, oh, well, that's because we have nothing in LoadView. So what do we want to do in LoadView? The first thing we want to do is we want to create this restaurant view. So we do that by calling restaurantView alloc and it with restaurant, the restaurant that we already have. And then we call size to fit. So the restaurant view will automatically size to fit properly in our display.

We had that Categorizer button below this, so we want to create that. Again, it's just a UI button type rounded rect. We're going to set its title to Categorize. We're going to add a target of ourself for the action Categorize, and again, for UI control event touch up inside.

And then we're going to tell it as well to size to fit. But actually, we want to tweak its size a bit. We want its height to be whatever it expects it to be, by calling size to fit. But we want to make its width as wide as our restaurant viewer is. So we get the frame of the button, and we then forcibly set its width to be our restaurant view's width. The next line you can ignore for now. I'll come back to it. It's a little confusing. And finally, we set the frame on the Categorizer button.

We then need to add these views somewhere. Well, let's add them to our custom centering view class. The centering view basically lays things all out nicely and vertically on a center line. And we're going to set up that centering view's width to be the main screen's width. We set the view controller's view to be that centering view. We don't need to retain it anymore.

And then we add our restaurant view and our categorize button to the center in view. So what do we get now? That looks a lot nicer. Unfortunately, it doesn't really do anything. We rotate. It doesn't. If we click the Categorize button, I'm not actually going to because it will crash.

The reason it crashes is because we did set up this selector before, but we didn't actually implement a categorized selector on here. So it will try to call that, and it will throw an exception. So the first thing I want to show you how to do is how to support rotation.

So we said earlier, should auto rotate to interface orientation? In our case, the only interface orientation we do not natively support is UI interface orientation portrait upside down. By default, apps on the phone that you see rotate will not support portrait upside down. It's kind of a weird thing. When you do that, the user then loses their spatial relation to where the phone is. So it's best to, you know, if you're going to be doing a portrait mode, only do it in the normal portrait mode, unless you have a reason to flip it upside down.

We then have the will animate first half of rotation to interface orientation and will animate second half of rotation from interface orientation. Both of these are going to tell the restaurant view size to fit to interface orientation. First stage, yes, and first stage, no. So first stage is just a parameter we're passing down to the restaurant view. So how does this work? Well, if we were to look at the restaurant view, So we have our basic setup stuff here.

If we scroll down to size the fit first stage, the restaurant view is pretty smart. It knows to change its width depending on whether it's in portrait or landscape mode by calling UI interface orientation is portrait. It then does some nice layout for all of its subviews as well.

It says, OK, well, actually, you want to be this width and this height, and you want to be over here and whatnot. All right, so you would think we're done, right? And when you rotated, Things look pretty good, right? Except for that you'll note that everything moved in the first half of the rotation.

By this 45 degree mark, they're done. There's nothing more for them to do. This actually looks a little weird to the user because things are moving not together with each other. So the way we need to handle this is we need to actually handle the first stage parameter that we've passed in here. We do that by saying, well, if we're in the first stage, then we actually only want to go halfway to where we want to go.

All right, well, what does that mean, really? Halfway is going to be this nice little function here. takes an old rect and a new rect and goes halfway for each of the points, for the origin and for the size. So when we do this now, We will not be able to because I need to uncomment that.

So now when we rotate, well, the subviews still aren't responding to that. But you'll notice that the main view animated very nicely between the two. So what we need to do is we need to make sure that our subviews also respect this first stage, second stage. So for the description view, before we set the frame, we need to call that. And for the title view-- And then finally, for the rating view for the stars, we want to change that.

So now, when we rotate, everything is moving very nicely from one orientation to another. But if you look really closely, I don't know if you can tell up there, everything's a little blurry. The description's a little blurry, the stars are a little blurry, and the title of the restaurant got blurry.

Why did that happen? We didn't really change much, right? Well, actually what happens is that when it's animating through, at that 45 degree mark, there are transforms applied to all your points. Your points are at 45 degrees. You're trying to set these points. All these points are floating point numbers, and things mess up.

So why do things mess up? Well, in the centering view, the way we've been centering is by setting the frame of the views. But the frame of the view at a 45 degree angle, where that origin is, it's not exactly precise. So the way we actually do this is we get rid of all of this stuff.

And we actually want to set the center of the view. We're rotating around the center of the view. So the center is going to stay a constant point, no matter what that transform is. So we grab the center. We make sure that the center is in the right place. And then we set the center of the subview to be that.

Now when we rotate, you'll see that everything remains nice and crisp, no matter how many times you rotate, because you are moving from a solid point. All right, so now this app supports rotation. That was pretty easy, right? There wasn't that much to it. A couple tips and tricks here and there.

What about the actual button? So before I showed you how to add the target, so now we actually want to respond to that target. We don't want to crash when the user clicks on the button. So we need to implement the selector that we had set up, Categorize. And for that, we're going to create another view controller called Categorizer here. And then we want to present modal view controller Categorizer animated yes.

So when we do this-- It nicely pops up a sheet. The sheet doesn't do anything, but it does dismiss nicely, so hey, that looks cool. What does the categorizer actually do? When you actually click on any one of those buttons, the categorizer is actually set in the interface builder. And when you click on any one of those buttons, it sends the categorize action.

So the first thing we want to do is we want to, you know, we're not actually going to be changing the model here at all. This is just a demo. But we want to animate this nicely. So the first thing we're going to do is track all the buttons that this one is not. So Japanese, Chinese, Italian, French, which one isn't the sender that was actually passed along, and add it to this other button's IVAR. And then we're actually going to animate the view of the restaurant label to the button.

So what does that do? Well, first thing it does is it grabs the destination rect. It then sees whether the rect is equal to 0, just to make sure that we are animating to someplace sane. We're going to begin ignoring interaction events, like we saw earlier. And then we're going to have a little trick here. We're going to be animating this restaurant label view down to the button, as you saw in the original demo.

When we do that, it's possible for the other buttons to be higher up in the view hierarchy than the label that we're animating. And so it might seem to pass behind some buttons and in front of others. You would expect it to always be on top. So what we do is we get all of our subviews, and we make sure to exchange the view that we're moving.

So exchange subview at index, sublayers index of object view, with subview at index, sublayers count, minus 1. This basically just moves us to the front. We're then going to create a keyframe animation, like we saw in that earlier slide. We need to find where we're actually moving this label to. So that would be the center of the button that we clicked, which we grabbed here.

Next, we're going to create a ballistic path, as I like to call it. So a CG path create mutable. We get the view's frame. We do some nice little math here. I'm not going to really go through how to create CG paths. Basically, this is going to make the view animate up and then back down again, as if you were throwing a ball into the hole.

We then need to add this ballistic path to our keyframe animation. So we set the path, we set the duration, we set the delegate, we set the fill mode and removed on completion, and then we add animation for key, and we release, and that's it. So what does this look like now? Now when I click on French, it goes very nicely there.

Well, that was exciting, but-- Right, two things here. One, we ignored interaction events again. And two, that didn't really look like the demo. In the demo, it kind of rotated and it shrank. The other buttons faded out, et cetera, et cetera. So how do we do that? Well, we've already set up this one animation here. You can set up multiple animations. And you can have the animations behave on the same layers that you've already animated.

So what we're going to do now, so we've added this keyframe animation, we're going to do the Rotate Restaurant and Dim Other Buttons animation. So we're going to make sure that it has the same duration as the keyframe animation that we had up here so that they behave together. We're going to set the animation delegate to ourself again.

We're then going to rotate the label view. We do this by getting the view's transform, and then applying a rotation and also a scale, because we want it to kind of shrink down as it rotates. And then we're going to set the view's transform. This is another animatable property on the view. It will automatically animate nicely to this. Finally, we want to take the other buttons and fade them out. So we saved off the other buttons before when we initially clicked Categorize. So we say button set alpha 0. And then we want to commit animations.

Now when we run this--

[Transcript missing]

So the first thing we're going to do is we're going to say, all right, well, was this our Rotate Restaurant and Dim Other Buttons animation? If so, let's begin another animation. Let's call it Fade to Black. Delegate to ourself, same duration to keep things nicely balanced. And we're actually going to fade out the name of the restaurant.

So now when we run this, awesome, we can see French. This was a great example of stacking animations on top of each other. As soon as the first animation finished, the next animation fired off. Of course, we are still blocking user interface interaction stuff, so we need to handle this finished as well.

So first we check, you know, is it equal to this string? Well, it's not. So is it equal to fade to black? OK, well, then we want to end ignoring interaction events. And it would be nice to also-- we've already categorized this, so call self-cancel on this view controller.

And that makes the view controller slide out, and we have control again. So that is the Restaurant Viewer demo. So pretty simple, right? For more information, Derek Horn is our application frameworks evangelist. His email is there, [email protected]. In addition, documentation from the iPhone Dev Center, as I'm sure you're all familiar with. Both of the samples that I used here are available to be downloaded, the simple animation sample and the restaurant viewer sample. There's some related sessions that you might want to see.

The first one is optimizing performance in iPhone applications. Now, one of the things I talked about here was set up all these different subviews. None of these examples really use drawRect very much. The restaurant view did a bit to do that nice rounded rect, but pretty much everything was done with subviews.

That's awesome for animations. It's awesome for maintainability of your apps. It can be not so awesome for performance. So sometimes you may actually want to do a drawRect versus using subviews. Sometimes it's actually more performant to use subviews. This talk will actually really give you a good idea of when to use this or that.

Another example talk to go to is the How Do I Do That? Tips and Tricks for iPhone Application Development. This will go into this stuff in more detail and present tons of other really awesome tricks. There are a couple of labs to attend. The iPhone Controls, Views, and Animation lab is today at 2:00 PM. And there's also a generic UIKit lab today at 3:30 PM, both in the iPhone lab.

In summary, use the built-in controls that we provide you. There's a lot of them. Look at the UI Catalog example. There's tons of examples of different controls that you can use. I only gave you three. Use multiple views to place your content efficiently. Use UI view animations to move content around as needed because they're very fast. They use core animation for you automatically. You don't have to worry about them.

And finally, I talked about this in the beginning, I want to bring this home again. Animations are not just nice candy. You really want to use them to present context to your users, so users know when you're moving from one thing to another. They keep a solid frame of reference via the animations.