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

WWDC08 • Session 923

Mastering Interface Builder

Tools • 59:52

iPhone and Mac developers depend on Interface Builder to design sophisticated user interfaces in a short amount of time. Learn to use this powerful tool more effectively as we guide you through application development from start to finish. See how you can quickly and easily localize your interface designs and master advanced features to speed your development.

Speakers: Matt Gamble, Jon Hess, Kevin Cathey

Unlisted on Apple Developer site

Downloads from Apple

SD Video (606.6 MB)

Transcript

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

Good morning. We're all here bright and early. Welcome to session 923, Mastering Interface Builder. I'm Matt Gamble, an engineer on the Interface Builder team, and I'll be joined on stage later by Jon Hess and Kevin Cathey. Today we're going to take you through some of the new features and some of the tips and tricks that you can use to be more productive working with your user interfaces using Interface Builder. I'm going to take you through exploring interfaces, some of the new features, and some features that you might not have known about for making navigating and exploring your interfaces a lot easier.

Now I'm going to touch on four different topics today. The first for exploring objects, we have the contextual navigation menu, which shows you all the various objects that are currently under the mouse cursor. The second is exploring connections using the connections panel. Baird will be exploring classes using either the documentation or the interface for the selected object. And finally, exploring the localizable strings of your document using the new Strings tool. So if we could switch over to the demo machine, let's get right into it.

Now you've probably all been in a situation where you've been either the newest member of a team or perhaps you've just had an existing project land in your lap. And either way you end up with a user interface that you've never seen before that you have to explore and find out what's in it, how things work, how everything's connected. So that's exactly what we're going to do today.

We have a simple little image editing application that has a couple of filters that you can use to adjust various attributes of the image. So we're going to take a look at the UI for this application and try to figure out how things are connected, what's in the UI, and what steps we would need to take in order to enhance this application or add a new feature. So I'm just going to quit this application.

And here we are in the Xcode project for this application. And we can see that in the resources section, we have our main menu ZIP. So I'm going to double click this to open it in Interface Builder. So the first step that we want to take is to explore some of the different pieces that made up that UI that we saw in the running application.

And probably a good place to start was that main window. So let's take a look in our document window. And we can see that there's this Canvas window. So I'll double click this to open it up in Interface Builder. And so here we have the main window that we saw in that application.

Now granted, we know that there's a window here. And when the application was running, we saw that there was an image that wasn't this icon that was in the center. So it seems like there might be a bunch of different things going on just in this little piece of UI that we have right here.

Now something that we can use to get a better understanding of what makes up this piece of user interface is to use the contextual navigation menu. To activate the contextual navigation menu, you simply hold the shift key and then right-click on what you're planning to investigate. Now we can see that we have a menu that shows us all the various pieces of UI that are underneath the mouse at this current time.

So we can see that we have the Canvas window, the content view of the Canvas window. Oh, okay, we can see there's a scroll view there, which we hadn't seen before. Might not have known that was there. There's also a Canvas view of some sort, the image view, which was displaying the image, and the cell of the image view.

So let's take a look at this bordered scroll view. In addition to showing us all the various objects that are under the mouse, it will also allow us to navigate over to those objects, some of which might have been hard to select otherwise. So I can simply select this item in the menu.

And then we can see that the focus of the inspector has changed to the scroll view. Okay, I can see why we couldn't see it before. We had the horizontal and vertical scrollers turned off. So if I turn those guys on, all right, that makes it a little more obvious that that's there. So probably won't be as confused next time.

This is a great way to explore the various objects that you have in your document, and especially when you have a case similar to the one that we had here where you have some elements that are obscuring other elements in your user interface. I know that I've used this a lot when perhaps I have views that are hidden right now that will then be added and unhidden programmatically through animations or various things like this. A lot of times these elements overlap, they're tough to see, and so this contextual navigation menu gives you a great way to take a look at all of those.

Well, the next thing we probably want to do is get a better understanding of how the various pieces of this UI are connected. And to do that, we could go to the Connections panel of the Inspector, which you probably saw if you were on the session on Tuesday. But what we could also do is we could right-click in the document window on this My Image Editor controller, and this will bring up the Connections panel.

The Connections Panel shows all the various connections that are incoming and outgoing that involve the selected object. At the top, there is a section of all the different outlets of this object that are connected. We can see the received actions that are connected as well. We also, something that you might not have seen before is this binding section. In this case, the referencing bindings, which are the Cocoa bindings between the image property of the focused object and this value property of what looks like the image view.

And this final section down here is the referencing outlets. So not only can we see the outlets that are defined for this object, but we can also see outlets that are defined for the other objects that are then connected to this object. So we can see both directions very easily in the connections panel.

Now, if I select this connection here, we can see that this is the binding between the image and the value property of the image view. Selecting this, you can then see that it's highlighted, and there's a path control that's appeared at the bottom. Now, this path control will show you the hierarchy from a top-level object all the way down to the object on the opposite end of this connection.

So we can see, similarly to how we saw with the contextual navigation menu, we again see the Canvas window, the Canvas window's content view, the scroll view that we can now see a little bit more easily, the Canvas view, and then again the image view, which is the other end of the connection. So this can make it a lot easier to see what you are connecting to. to get a better understanding of how these objects are related.

Now finally, if we take a look at this Adjust Hue connection in the Connections panel, you can see that when it was selected, there's a little arrow that might be a little bit tough to see for those of you in the back, but next to the connection well, there appeared a little arrow, which is the refocusing arrow, which now allows us, if I click this arrow, the Connections panel will then change to refocus on the other end of that connection, which in this case would be the Adjust Hue toolbar item.

So if I click this, refocus, we can see that now it's focused on the toolbar item, and the sent actions is connected to the Adjust Hue action of our controller. So this can be a great way to navigate around to get a better understanding of how the different objects are connected by simply staying in the same Connections panel.

I also find that this is very useful when you want to make a bunch of different connections to some objects in your user interface. So you can simply open the connections panel, start making connections right next to where they actually are rather than coming all the way over from the inspector. And then once you've made a few connections, you can start navigating through the panel to the other objects and then make subsequent connections as well.

[Transcript missing]

We can see that Xcode has opened up the interface for this object and we can take a look at all the various properties. We could add any IB outlets that we wanted, any IB actions, then quickly go right back to Interface Builder and they'd all be there for us to connect.

So this can be a much easier way to navigate. I'm going to close this window and return over to Interface Builder. And similarly, I said that we also have the documentation for selection. So if I select this scroll view right here, and the same way that we would in Xcode if we wanted to do the same thing, we'd hold the option button and then double click.

It's not being agreeable today. You can always use the menu item, click the documentation for selection, and we go over and we can see it right here. This can be an easy way to gain a better understanding of and make use of the great documentation that we have for all these various classes.

Now the one final thing that I'd mention, switching back over to Interface Builder, I mentioned that we could take a look at some of the strings using a new feature in Interface Builder. Now in this case, we've been looking around and we could see that it's especially obvious in the menu when the application was running.

We saw that everything had that My Application or that generic name that we get when we use one of the templates in Xcode. So now that this project is ours, we want to make it a little bit more personal. We probably want to change that. Well, an easy way to do that is to go to the Tools menu and open the Strings tool.

So here we can see all the localizable strings in any of the open documents in Interface Builder. You can then refocus it to particular documents or keep it on all of them. And in addition to filtering, we can also go to the Edit menu and find. And we see that we can now find any string, any localizable string, in any of the open documents in Interface Builder.

So in this case, that new application is not fantastic, so let's get rid of that. So by finding, we can see that it selects the first match, and I can toggle through the matches, same as I would with any other find. But what I can also do in this case that will help us out a lot is I can change the find pop-up to find and replace, and change this to image editor, and then simply do a replace all. This will replace all the instances of new application with image editor.

and great, there we go. So when Kevin does his demo later, we won't have that awful my application or new application name in there. So you can see this can be a great way to not just take a look at the localizable strings in your document, but also a way to make large changes across many different nibs that you have open in Interface Builder. So it can be very handy. So if we could switch back to the slides.

Let's just go over quickly a few of the things that we touched on during that last demo. First of all, we talked about the contextual navigation menu. We mentioned how it shows the hierarchy of all the objects that are currently under the mouse, which can be great for navigating and can be particularly useful for discovering some objects that might have been obscured by other pieces of our UI. So it allows quick navigation and selection.

We also mentioned the connections panel which displays all the different connections to the selected objects, IB outlets and IB actions. We also saw that it includes Cocoa bindings, both incoming and outgoing. It also includes all the different connections that reference the selection, so we have both the incoming and the outgoing connections. We also saw that we now have a path control in the bottom that shows the hierarchy of the opposite endpoint of the selected connection. So we can get a better understanding of where this object is in our user interface.

And then finally, we saw that we could refocus the connections panel by simply clicking the refocus arrow, not having to open yet another panel by clicking another object at a different part of our UI. This makes it a lot quicker to change the focus of the connections panel and get a better understanding of how our objects are connected.

We also saw the Jump to Interface feature, which allows us to jump to the interface declaration for the selected object, and just like in Xcode, we can just hold the command key and double click. and we also saw that we could do the documentation in the same manner and hopefully you'll have better luck with the option key than I did.

and finally the Strings tool, which allows us to view all the localizable strings in any of the open documents that we have in Interface Builder. also allows us to edit the strings directly in the table so we don't have to click on the object and go to the inspector. You can also check spelling. You probably saw the red squiggly lines that were under some of the pieces, some of the strings that were in the table.

You can then filter down, you can find, and as we saw, you can find and replace across any of the open documents in Interface Builder. and also something I didn't show is if you can double click one of the sections of the table that is not the value that you would change and it'll actually open it in Interface Builder for editing.

So we've taken a look at exploring our interface through exploring the different objects that make up our UI, as well as how they're connected and some of the strings and other ways that we can get a better understanding of what makes up our UI. But we should now-- let's delve a little bit deeper into what are these Interface Builder documents and how do they work? And to do that, I'd like to invite Jon Hess on stage.

and I are going to explain how the interfaces you design in IB go from objects in the library to objects in your document to interface elements in your running application. So what are some of the objects in an interface? What composes a nib file or something you design in Interface Builder? There's objects.

There's proxy objects that we're going to talk about. The files owner and the first responder are two of them in particular. There's class information that you might type into Interface Builder manually or that's going to come across from Xcode. And there's connections that you drag between the objects in your interface.

So how are the objects in Interface Builder created? Just what happens when you go to the library and you grab a hold of a push button and drag it into your interface? You might have assumed that Interface Builder was working with something like a work-alike of a button, something that looked like a button but was editable in Interface Builder. Actually, Interface Builder just uses regular buttons, and it does all the same sort of things that you would do.

So when you drag a button from Interface Builder's library into your running application, we do just the same thing that you would do. We allocate and instantiate a button with an alloc and an init message. If you double-click on a button's title in Interface Builder and customize it, for example, in this case, we'll change it to OK, Interface Builder is going to do just the same thing that you would. We're just going to call the setTitle method of the push button to change the title.

If you use Interface Builder's inspectors to further customize attributes of the objects you're working with in Interface Builder, for example, to change a button to the default button by changing its key equivalent to return, we're going to do just the same things that you would do. We're going to call the setKeyEquivalent method of the button to change the key equivalent.

So in addition to the regular objects that you work with in Interface Builder, there's two proxy objects. They're the top of all of your Cocoa documents, the files owner and the first responder. And if you didn't understand them, I'm going to explain to you exactly what they're for right now. Let's take a look at them in closer detail.

The first object is the files owner, and this object is critical to understanding and using Interface Builder effectively. Every Nib file is going to be loaded somehow, well, every Nib file that you're going to use. And when it's loaded, there's going to be an object that's responsible for doing the loading, and that object is the file's owner.

It's typically an instance of one of your custom controller classes, and by existing outside -- and by loading the Nib file, it must exist outside of it, so the instance that you see in Interface Builder is actually a placeholder or proxy for that object that exists that loads the Nib file.

Nib files on Mac OS X are loaded with the method NSBundle loadNibNameOptions. On iPhone OS they're loaded with the-- I'm sorry. On Mac OS X, nib files are loaded with the NSBundle method loadNibNamedOwner, and on iPhone OS, they're loaded with the method loadNibNamedOwnerOptions. Two very similar methods. They both take an owner property.

And when you load a nib file, you specify the owner. That object that you specify as the owner is what the file's owner proxy in Interface Builder represents. And you'll use this to tie the objects inside of your Interface Builder document to all the objects that exist outside of your Interface Builder document.

It's best to explain this with an example. When your application starts life right after the user double clicks on it in the finder, there's just a single solitary instance of NS application, and it's just starting up. And the first thing that it does and David Koehn. The first step is load the main nib file. It invokes the NSBundle method, load nib named, owner, loads the main menu and passes itself as the owner.

The nib file is loaded from disk and all the objects from it are reinstantiated And inside there, there's a files owner proxy. By virtue of the application passing itself as the owner parameter to the loadNibNamedOwner method, the files owner proxy is resolved to the application. All the connections that you make in Interface Builder that involve the files owner are now going to be made to the shared application. So this is typically how you connect things like your app delegate.

Once the Nib file is loaded, it's not needed anymore, and we're just left with the objects that were instantiated from our Nib file. Now, in this case, we've got a main window and a main menu, and we might do something in response to some menu action or button click, and it might cause us to want to load a second Nib file.

If we were to do that, we might, from our app delegate, invoke something like NSBundle, load nib name, secondary, and pass the app delegate as the owner of this secondary nib file. If we did that, the nib would load, the objects inside it would be instantiated, our file's owner proxy would be resolved to our application delegate, and we'd be able to have our connection that we would have made in our nib file from our file's owner to our second window resolved to our app delegate.

So in this way, the app delegate existing before the nib was loaded is able to load the nib file and refer to objects inside of it, and connect those objects back into the rest of our application. After we've done this, messages would be able to flow to and from the objects from the secondary nib to the rest of our application by means of the file's owner.

So the file's owner is the conceptual owner of the nib file, and it's a conduit for all the objects inside the nib file to send messages in and out with the rest of the objects in our application. A second critical object to use to master to use Interface Builder effectively is the first responder. What is the first responder? The first responder represents the object on a window that first receives keyboard events. It's also used as the target for buttons and controls and menus that don't have specific actions, things you'd want to target at all sorts of things.

Let's also take an example to define that. Here I have on the display a diagram of maybe a simple chat application. It has a list of buddies. For example, maybe that's a table view. It has a message text view that might show the chat history that I have with one of my particular buddies and a composition text view that I might use to type a message. If the user were to click into the message text view, the message text view would become active, and it would be the first responder in our window.

Well, all responders have next responders, and together the first responder with its next responder and that next responder's next responder form a chain. In this case, our message text view would be the first responder, and its next responder would be a scroll view, followed by our Windows content view, our window, our window controller, and the responder chain would continue through the rest of our application.

If at this point the user were to use the Edit menu, well, let's just think about this for a little bit. In the Edit menu, there's a Copy command. And that Copy command can do all kinds of things. It can be targeted at every text field in our application and many objects besides text fields.

And if we were to try to connect the Copy menu item ourself, maybe to our application or app delegate, it might get kind of complicated. Like, where would we want to send that copy message? How would we perform copy? We'd have to know about all sorts of things in our application to perform copy effectively.

So if you go inspect your main menu and you look at the edit menu and you find the copy command and you bring up the connections panel like Matt showed earlier in the demo, you'll see that the copy menu item is actually connected to the copy action of the first responder.

This means that if the user chooses copy from the edit menu, a copy message is going to be sent to the first responder, in this case our message text view. So if the user chooses copy, a copy message is sent to the first responder. And in this situation, everything's great. Text views know all about copy. They take their selected text, they bottle it up, and they put it on the paste board and they make it available for the user to paste later. So the text view successfully handles the copy message and we're done.

If later in the application's life the user were to click into our buddy's table view, the first responder would change. It would now be our buddy's table view. And the responder change -- the responder chain, by virtue of its head changing, would update to reflect that it now starts with the buddy's table view and continues on through the buddy's table view's scroll view and up through the rest of our window.

At this point, the user might again go to the Edit menu and choose the Copy command to mean something like copy the email address or instant messenger name of the buddy they're working with. and a copy message would again be sent to the first responder. In this case, though, the first responder is a totally different object. It's our table view.

And we might have our table view work together with its data source in order to provide an implementation of the copy message. And this would allow the user to perform copy in our application on both the text view and the table view without us having to have any complicated dispatch logic to decide what to do when the user presses copy.

Now we have this whole responder chain now, and so far we've only looked at messages that target the head of the responder chain. What happens if the user were to go to the file menu and choose something like send file? This is a common kind of action that you might want to do in instant messaging application.

Well, if our buddy's table view was the first responder, the send file message would be sent to the head of the responder chain. But buddy's table views probably don't know much about sending files. That's a pretty specific task and you wouldn't want to handle that sort of action in a table view.

So the buddies table view would decline to handle the message and the message would be delegated up the responder chain. The scroll view would get a shot at the send file message, but scroll views also aren't particularly suited to handle sending files. So this message would continue up the responder chain to our content view, then to our window, and to finally our window controller. Now, a window controller would be a great place to handle this message. The window controller would have custom application logic.

It would know all about instant messaging and it would understand that sending files was a common action. It would have an implementation of the send file message and would intercept this message and perform the send file action, perhaps by bringing down a sheet to ask the user which file they'd like to send.

Now, a great thing about targeting this menu item at the first responder means that if our chat application were to have multiple windows, the send file action would always be sent to the frontmost window and to the head of the responder chain in that window, and it would be able to travel up to the window controller. So exactly the right window controller would always get to handle the message.

Also, it wouldn't matter which field was currently active in that window. It could be the message's text view, or the composition text view, or the Buddy's Table view. In any case, the send file command would propagate all the way up to our window controller. So the first responder is great for avoiding complicated dispatch logic of commands that come from menu items and maybe buttons in your applications.

So far we've talked about, well, the first responder doesn't point to the specific instance, doesn't have a specific class, but often when you work in Interface Builder, either with the proxy objects or custom objects that you've added yourself, you're going to need to work with specific classes. And in order to do this, you probably use Interface Builder's identity inspector to set the custom class of an object. Just what does it mean when you set the custom class of an object in Interface Builder? When you set the custom class of an object in Interface Builder, there's a promise going on between you and Interface Builder.

It happens one of two ways. With a regular object, when you set the custom class, Interface Builder makes a promise to you. And it says, when I instantiate this class at runtime, I promise to make an instance of the class you specified, as opposed to the instance that currently exists in Interface Builder.

When you set the custom class of a proxy object, like the file's owner in Interface Builder, you're making a promise to Interface Builder. You're saying, "Interface Builder, when I load this NIM file and you resolve this proxy object, I promise it's going to be an instance of the class name that I just typed in." By doing this, Interface Builder is able to determine the class of the object you're working with and present all of the outlets and actions for you to connect to your other objects.

So, that brings us to connections. What does it mean when you control drag or use the connections HUD to make a connection in the interface builder? Well, it's another promise. Interface builder promises to you that it will connect these two objects at run time. It doesn't make the connections just yet, right? Your file's owner is a proxy.

It doesn't have something like a window outlet. It won't have a window outlet until run time. So it records that these two objects should be connected. And at run time, it'll do something similar to calling, for example, if we connected a window outlet of our file's owner or our controller to our window, we'll call the setWindow method to connect them.

so how exactly are the connections you make in Interface Builder actually established? Well, there's three kinds of connections you'll primarily work with in Interface Builder. There's outlets, actions, and bindings. Outlets are perhaps the most interesting. When Interface Builder goes to establish an outlet between two of your objects, it will first look to see if there's an appropriate setter method that matches the outlet name. For example, if your outlet name was window, it would look for a setWindow method, or an Objective C2 named window. And if such a method exists, it would call it in order to establish the outlet.

If no such method exists, Interface Builder will look to see if your object has an instance variable named window. And if it does, it will connect them. If it doesn't have either of these, then you've probably broken your promise to Interface Builder that you made when you set the custom class.

Actions and outlets, or I'm sorry, actions are connected by just calling, for example, in Cocoa, set target, set action against the NSController and NSCell, or on Cocoa Touch using the appropriate UI control method, I believe it's add target selector for event mask. Bindings are established the same way that you would with bind to object with key path options. So we make all the connections just the same way that you would. We just allow you to draw them up in Interface Builder and we wire them up when the Nib file is loaded.

So the next important piece of information to understand is how is a NIM file saved and loaded? I mean, you make these objects in Interface Builder, but somehow they have to get to disk and back into your application. Well, we do this in Cocoa with a protocol called NSCoding. All the objects that you work with in Interface Builder implement NSCoding. NSCoding is a simple protocol. An object called an NSCoder goes and visits each of your objects and allows them to either save or load themselves to data.

So to implement saving, all the objects implement this encodeWithCoder method. And they first call the super implementation of encodeWithCoder to give their super class an opportunity to encode any of their interesting state. And then they follow that call to super by encoding their own interesting state, in this case, a title. So they would call against the coder, encodeObject, title, for key.

And they might use title for the key. Loading the objects works much the same way. The data file is loaded from disk, an NSCoder is given the data and reads through the data to find the objects that it serialized. And as it finds each one, it instantiates the object and sends it an init with coder message. And then it's the object's -- the object then has an opportunity to decode all of its interesting state back out of the archive.

So in this example, we'll give our super class a chance to init with coder. And then we'll follow that by decoding our own attributes from the coder. Now, this is really important if you go to make your own interface builder plug-in. If you go make your own interface builder plug-in, you'll need to implement both of these methods on all the objects that you integrate into interface builder.

So let's put all that together. When you drag objects out of the library, Interface Builder instantiates them just the same way that you would with alloc and init messages. When you customize the objects, either on IB's design surface by dragging them around or resizing them or using the inspectors, we call all the same setters that you would.

When you connect the objects, Interface Builder simply records that the two objects are connected and promises to make the connections at runtime. When it's time to save, Interface Builder instantiates an NSCoder, in this case a keyed archiver, hands off all the objects from the Nib file. All the objects implement the encode with coder to encode their interesting state, and the data file is written to disk.

and when it comes time to load the file, another coder is instantiated, handed the data file, it creates objects from the objects that are described in the data file and all those objects implement initWithCoder to decode all their interesting state, the same state that you can figure in Interface Builder. Then Interface Builder establishes all the connections between the objects by doing things like setting their instance variables or calling their setter methods and sends the awake from nib message to all the objects.

So I just spent 15 minutes telling you all about nib files. But if you're working with the latest set of Xcode tools, you're probably working with ZIP files. What's the difference? Just what is a ZIP file? A ZIP file is everything that's in a nib file plus more, and it's really optimized for the development time experience. It has all the runtime content, so all the stuff that you designed in Interface Builder. and Kevin has design time content too. For example, which windows were open, which buttons were selected, what custom user guides you had added, things like that. It's also diffable XML.

Works great with SCM systems. In order to provide the best design time experience, we save a lot of information in the nib file, and we save it as this diffable XML, which is very verbose. It's kind of large. When you ship your applications to your users, you don't want to ship large Interface Builder files.

You want to ship small, fast Interface Builder files. And that's what Nib files are. They're binary files that are optimized by Interface Builder and Xcode during your Xcode build so that you can ship tiny, small Interface Builder files to your users to give them the best performance possible.

So that covers how Nib files work. Let's go ahead and move on and look at some important differences between Cocoa and Cocoa Touch. Now, largely, when you use Cocoa or Cocoa Touch for Mac OS standard iPhone with Interface Builder, everything is going to be the same. There are just some small minor differences that I'm going to cover right now. The first thing that you'll probably notice if you use both platforms is that there's different proxy objects available on each of them.

On Mac OS X, you're going to immediately notice there's an extra proxy object available at the top of every single one of your nib files. And that's the NSApplication instance. This is a very simple proxy. It just represents the shared application that exists in every Cocoa object. And when you load a nib file, when you use a nib file with Interface Builder and Cocoa on Mac OS X, you can always refer to your shared application from any of your nib files by means of this shared proxy.

For Cocoa Touch, we have a custom proxy object. You can use this to represent any object in your Interface Builder you'd like to. You do this by going to the library and finding the proxy object. It's in the Objects and Controllers section. You can drag this to your interface. And once you do that, the next most important thing to do is use the attributes inspector to set an identifier for the proxy. In this case, I might call it My Controller ID.

That means I've got this proxy object in Interface Builder. It's not really a My Controller ID, and it's not going to be created when this nib is loaded. What's going to happen is it's going to refer to some object that was created before this nib was loaded. And at runtime, we're going to do that by loading the nib manually with NSBundle, loadNibNamed, owner, options. And we're going to pass in an interesting dictionary as the options parameter.

The options dictionary is a dictionary of options, and in there is going to be another dictionary, this one that I'm specifying right here. And it's going to be a list of keys and values. And the keys are going to be the same IDs that you typed into Interface Builder's attributes inspectors for each of your proxies. So here we have a key, my controller ID. And the value is going to be some object that you would like that proxy instance to resolve to.

We're going to wrap that in the options dictionary that we're handing to the NSBundle nib loading method. And we're going to use the key UIDnibProxiedObjectsKey. Once we do that, we can load the bundle -- or I'm sorry, we can load the nib by way of the bundle and pass our options dictionary that we've created as the last parameter. This will allow Interface Builder to resolve that custom object that you designed to a proxy object you were specifying that is just outside of your nib file. If you find yourself wanting to connect to some parent controller, this is a good way to do it.

An important difference between Cocoa and Cocoa Touch is the retention of the top level objects. If you load a nib file manually on Mac OS X with the NSBundle loadNibNamed owner method or one of the NSNib methods, you're responsible for sending a release message to each of the top level objects.

This isn't the case for Cocoa Touch. We made things a little simpler. On Cocoa Touch, all the top level objects are auto released. So you don't have to send a release message to something you didn't specifically send a retain message to. This also means that in order to keep these objects around, you'll want to retain them explicitly. This is nicely balanced by something I'm going to show you in a slide just coming up.

Outlets. How are outlets established on the two platforms? By and large, they're established the same way. We look to see if there's an appropriate setter method or Objective C2 property, and if there is, we invoke it in order to establish the outlet. But if no such setter or property exists, on both platforms we will end up looking for an instance variable with a matching name and set that directly.

Now, the difference is, well, on Mac OS X, we have a technology and we have it on iPhone OS that we really love, and it's key value coding. And we've been using it a lot lately. It's really handy, and if it isn't something you're familiar with, you should look into it, because it makes all sorts of dynacism really easy in your application.

So on Mac OS X, when we establish those outlets, we do it manually. We look for a setter by using Objective-C runtime methods, and we look for an instance variable by using Objective-C runtime methods. You could use those methods, too. They're nothing private. But we prefer to use set value for key, because that's what a lot of our developers expect, and that's what we use on iPhone OS.

And there's one important difference between the two methods, either manual or set value for key. And that's when you don't have a setter. On Mac OS X, if you don't have a setter, when instance variable, I'm sorry, when Interface Builder connects one of your instance variables, it's an outlet, it won't be retained.

On iPhone OS, it is retained. That's the default behavior of set value for key. So on iPhone OS, that nicely balances the fact that top level objects aren't owed a release message. You can simply just have an outlet to each of your objects to retain them. The best practice here is on both platforms to always provide a setter method or Objective-C2 property. That means the decision of whether to retain or not is always in your control and will be obvious from your source code.

A second small difference between the two platforms is the awake from Nib message. On Mac OS X, every object represented in the Nib file with the exception of the application, even including the file's owner, is going to get that awake from Nib message. Now, you may have noticed this means that sometimes the awake from Nib message can be sent to an object more than once.

Not more than once per Nib file that's loaded, but more than once during the lifetime of the object. So if an object participates in Nib loading multiple times, for example, by being loaded from a Nib and then turning around and loading a second Nib by being the file's owner, it would receive the awake from Nib message twice.

This might kind of catch you off guard. You have to be prepared for that on Mac OS X. We've simplified this on iPhone OS. On iPhone OS, every object is only going to get the awake from Nib message once. And we treat it as a real initializer. You can call through to super. The method is implemented on NSObject. You should call through to super.

This allows you to easily, if you have kind of deep class hierarchies in your application and you're loading some of those objects from Nib files and you want to perform a wake from Nib on both your class and your super class, it makes it obvious when to call through to super, always on an iPhone OS.

Now the takeaway point here is that the frameworks always provide specific over alternatives to Awake from Nib for objects that behave as the file's owner. And you should use those. So for Cocoa, an example would be NSWindowController, which provides a method windowDidLoad. If you're using a window controller and that window controller is loading a nib on your behalf, you can easily move your code from await from nib into windowDidLoad. And that'll prevent any sort of accidental double call to await from nib for you.

And if you're using Cocoa Touch, you can use UIView controller's viewDidLoad method to react to your view being loaded from a nib file. This is preferred in both cases to using the await from nib with the file's owner. If you're loading a nib file manually with one of the NSBundle or NSNib nib loading methods, you can simply just do your code right after invoking the nib loading method. That's equivalent to doing it within an await from nib on a file's owner.

That covers all the differences for Cocoa and Cocoa Touch when working with Interface Builder. So I hope you'll see that while using Interface Builder for both platforms, by and large, you'll have exactly the same experience. And everything you learn on one platform will directly transition to the other platform. Right now, I'm going to turn it over to Kevin Cathey so he can tell you about everything we've learned today.

Thanks, Jon. So I'm Kevin, another Interface Builder engineer. What I would like to focus on for the rest of the session is how we can use some of the things that Matt and Jon showed us to improve our existing applications. Now, in particular, what I'm going to be doing is I'm going to be improving the image editor application that Matt was showing to us earlier. And the item that I want to improve in this application has to do with when we instantiate objects. So, let me explain.

As Jon was mentioning, when your application launches, your main menu nib file is unarchived and all of the objects in it are instantiated. All of them. So the problem comes when you have a bunch of top level objects in your main menu nib file that you don't need right away. And so when your application launches, you're taking time and memory to bring those guys into your application.

In the case of our image editor application, as you can see on screen, we have a bunch of filters you can apply to our images. And for each filter, we have a sheet and a controller object for that filter. Right now, all of these objects are sitting in our main menu nib file, so that when our application launches, every single one of those objects for our filters have to be brought into memory. But what happens if someone never wants to adjust the brightness of their image? Well, the objects for that particular filter are just taking up space.

So here's kind of the takeaway point that I'm going to give early on, and that's the goal in all this is that we want to wait to instantiate objects until the user really needs them. Now, you might also have heard this as being called lazy initialization. This is one of the things that I love about being a software engineer. We get applauded for being lazy. That's awesome.

So in the case of our image editor application, what we're going to be doing is we're going to be pulling apart some of the pieces of our nib file. And you might ask the question, well, how do we do this? The process is threefold. The first thing we need to do is create separate nib files for each set of objects that we want to lazily initialize.

Now, once we have this nib file, we want to pull it into memory. And to do this, we're going to be writing a little bit of code. Now, since we've initialized some objects, we need to make sure that we clean up after ourselves. And I'm going to be showing you using the policies that John was explaining to us just a little bit ago.

So let's go ahead and go over to the demo machine, and I'm going to show you how to do this. All right. Now for those of you who might have come in just a little bit late or maybe you haven't had enough coffee, I know that's often the case with me in the morning, I'm going to review the application for us.

We have our main Canvas window here. It's got an image. We can adjust the attributes of this image by bringing down these different sheets for our filter. What I'm going to focus on for this demo is how to lazily initialize the objects needed for the brightness controller. However, the process is just the same for the hue controller and other filters you might add to this application. So to do this, let's go ahead and go to Interface Builder and start off.

Here are the two objects that I was just mentioning that we're going to need to pull apart from our nib file. We have our brightness controller and our brightness sheet. So, step one, create a new nib file. So, we go under File, New, and I'm going to create an empty Cocoa nib file.

Now getting these objects over to this new nib file is as simple as dragging and dropping.

[Transcript missing]

and as you can see if I click on the end points for these connections, they all go back to our brightness sheet. Awesome. Now before I get any farther, I want to save my file.

And I'm going to save this into the same place as our other file, our main menu, and I'm going to call it Brightness Sheet. Let's go ahead and save that. and now Interface Builder is prompting us and asking us, hey, do you want to add this file to your Xcode project? You bet we do. So let's add it.

So here comes the first little tricky part of this thing, and that has to do with the files owner. As Jon was mentioning, there are those three different proxy or represented objects we need to know about when working with Nib files. The files owner, the first responder, and the application.

Recall that the files owner is the object that is put in charge of your nib file, kind of like a babysitter over a group of kids. Just as when you're choosing a babysitter, you want to make sure that you choose the right babysitter, one that makes sense for the job. In our main menu nib file, we have this image editor controller which controls the logic of our application. We want this to be the babysitter for our new nib file.

How do we point the instance of this image editor controller over to the file's owner of another file? Since the files owner is a proxy object, all we need to do right now is tell Interface Builder the files owner is going to be of this class. When we instantiate that file and code, we'll actually provide the actual instance that we're going to need.

So to set the custom class for the files owner, go to tools and the identity inspector. And I'm just going to type in my image editor controller and it will fill it in for me. And now files owner knows about the outlets and actions that the image editor controller can have. Thank you.

So the next step that we need to do is we need to connect up our files owner back to our brightness controller. And some of you might be scratching your heads going, but Kevin, I thought that you said that connections persist as we drag objects over to new files. That's totally true. However, remember, we didn't actually drag the instance of the image editor controller over to this new file. We're just saying the proxy object or the files owner is going to be of that type. So we need to reconnect them.

Now, to make sure I don't miss any of the connections, I'm going to go back to my main menu nib file and bring up the connections display for the brightness controller to make sure I get all the right ones. So right now I'm looking for any connections between the image editor controller and the brightness controller. And to do that, I'm going to be using the reverse outlets and the outlets sections of the connections display. So right away, one jumps out at me.

Okay, editor controller. And that certainly goes to our image editor controller. If I scroll down to the other ones of these, that's not it. That's not it. Ah, there's one more at the bottom here. So there's two outlets that we need to reconnect. So let's go back to our new nib file and do that.

When doing this, I'm going to take advantage of the control drag and connection feature. If you hold down the control key and drag from one object to another, you can connect things really quickly. In this case, it's saying, "Do you want to connect the outlet brightness controller?" Certainly I do. And I can do the same thing in the reverse direction. Hold down the control key, drag into FileZoner, and connect into Editor Controller. And now, our FileZoner and our brightness controller are now back in sync.

and step one is just about done. We've created a new file, dragged the objects in, set the file's owner. And the next thing we get to do is the fun part. We get to delete the original brightness controller and brightness sheet from our old file. And now it's two less objects that our application has to worry about when launching. So let's save both of these.

So step one of the process is now completely complete. And what we want to do now is write a little bit of code to bring this file into memory. When the user selects the Adjust Brightness toolbar item, as I see in my window up here, I want this new file to launch and the sheet to come down just as it had before.

If I bring up the connections display again on my image editor controller, I see there's this adjust brightness action. And this is where I want to instantiate this object. I can use the command double click that Matt was showing to us earlier to jump to the source code for the image editor controller.

Now, also as Matt mentioned, what's great about this feature is that it also works in Xcode. So we can do the same command double click to open up the implementation for the adjust brightness method. and now here we are. So now we actually have to write our code to instantiate this file. There are a couple ways to do this and I'm gonna highlight two of them and then we're gonna actually do one of them. As Jon mentioned, one of the ways that it's typically done is using NSBundle.

Now he also said that all top level objects in a nib file on Mac OS X are owed a release message. So some way we need to get to all those top level objects and send them a release message. There's another class called NSNib which provides a convenience method for us to get those top level objects, send them a release message so that we don't have to go scouring through other parts of our nib to do so. So that's the particular class that I'm gonna be using for this demo.

Part of lazy instantiation is that we don't instantiate our objects until we need to. And we don't want to instantiate them if they're already there. So we want to instantiate them when they're nil. So that's the first part. If our brightness controller hasn't been created yet, let's go ahead and load that nib file.

Now, creating the code for this is about a four-step process. The first step is actually creating the instance of the NSNib class that we need. You can think of NSNib as kind of like a nib file loader. It just brings in data for that nib file, but doesn't actually put the objects in the nib file into memory quite yet. So as you can see here, I'm just initializing a nib instance and pointing it to the brightness sheet nib file that we created earlier. And I'm doing it in the main application bundle.

The second step is to actually bring the objects into memory. And this can be tricky, so let's go through this. On the nib instance that I created, I'm going to call instantiate nib with owner, and here's where I specify the actual instance of the file's owner that we wanted. Remember that we set the class in Interface Builder to the My Image Editor Controller, and now we're providing that actual instance right here.

Now the second argument might throw you off a little bit. So let's talk about this. This is where we get the top level objects that are in our nib file. What's happening is when we instantiate this NSNib instance, the NSNib class, our instance, is creating an array for us and populating it with the top level objects and sending it back to us. So that's why I'm passing in a reference to an NSArray that, again, the instance will fill in for us.

The next part, of course, is to send all of these objects a release message. All the top level objects a release message. Some of you who are familiar with memory management on OS X might be wondering, wow, why are we sending them a release message right away? Won't this make all these objects go away? Right now our objects are owned by two things.

First of all, it's the implicit loading for Mac OS X, so all top level objects are owed a release message. And secondly, they're owned by the array. If you release it, if we send them all a release message, right now they're only owned by the array, so they're auto released. We get the same behavior as you would have on iPhone OS.

and David . Now if you add outlets for these particular objects in our top level, you will also get an additional retain count if you happen to retain those objects. But that's all going to be handled by our properties. For now, it's very safe and encouraged to do it, to send all those objects a release message right here.

The final step of this is that we no longer need our nib loader. That's brought our objects into memory. We can get rid of that. We send it a release message. So, a lot of code. Let's step through it one more time just to make sure you understand it. First, we created our NS nib instance. This loads the data for a nib file up for us.

Secondly, we instantiated the objects in that nib file by taking them out of the nib file and putting them into memory. And then we got an array back of all the top level objects. Now, since all these top level objects deserve a release message, we're providing that for them. And finally, since we don't need our nib loader any longer, we can safely release it.

So now we've completed, hopefully successfully completed, the final two steps of our process. We've initiated our nib file and code, and then finally we've cleaned up any resources. So let's run this and see how this works. Let's go build and go. When I click on this, it brings down the brightness sheet just as before. I can still edit all the attributes.

But instead of our application having to spend time to bring into memory all the objects for the brightness sheet, we've done it when the user needs it. We have a faster launching application. We're using memory more efficiently. And our development environment is now more logically organized. So let's go back to slides.

Can we go back to slides possibly? Awesome. Thank you. All right. So let's review again one more time what we've done in this demo. We've optimized the launch time of our application by lazily initializing objects that we don't need right away. And again, to do that, it's a three-step process. You create your nib file, you instantiate it in code, and finally, you clean up any resources that you need.

So I've completed my part of the session and let's take one more grand tour of all the things that we've seen today. First, Matt gave us kind of some tips and tricks for being a power user of Interface Builder. Now secondly, Jon took us on kind of a behind the scenes tour of how nib files work and some of the differences between Cocoa and Cocoa Touch. And finally, we took all those pieces, put them together, used some of the new features of Interface Builder to lazily initialize some of the objects in our application.

If you have any questions about Interface Builder, about this session, about our developer tools in general, we encourage you to contact Michael Jurwitz, our developer tools evangelist. If you need direct access and quick access to Interface Builder help, we have some great documentation which is available from right within Interface Builder. To access that, just go to the Help menu and go to Interface Builder Help.

We didn't have a session on it this year, but for those who are interested in writing their own plugins for Interface Builder, there's also great documentation for that as well. So we encourage you to check that out as well on either the ADC website or the documentation included with Xcode Tools.

If this session piqued your interest about Interface Builder, you can go to the Mastering iPhone View Controller session to see how you might use Interface Builder to create view controllers for the iPhone. That's in Presidio today at 2:00 p.m. If you want some more hands-on help with Interface Builder, maybe directly with some of the projects that you're working with, there are a number of labs you can go to. For those interested in some of the advanced parts of Cocoa Bindings, come to the Advanced Cocoa Bindings Lab. And for those that need help with the tools or their own applications, there are two developer labs, one today, one tomorrow.