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: wwdc2004-317
$eventId
ID of event: wwdc2004
$eventContentId
ID of session without event part: 317
$eventShortId
Shortened ID of event: wwdc04
$year
Year of session: 2004
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC04 • Session 317

Interface Builder: Techniques for the Advanced User

Development • 50:08

This session on Interface Builder will cover advanced topics such as building and debugging Cocoa palettes, so that you can leverage Interface Builder more effectively.

Speakers: Chuck Fleming, Jon Hess

Unlisted on Apple Developer site

Transcript

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

So it's the end of the week. Your brains are probably filled with lots of ideas about how you might use some of the new APIs that have been discussed. A lot of the new technology that's been demoed. And what I'd like to do today is talk about the tool that hopefully you'll be using, one of the tools anyway, that hopefully you'll be using to implement some of your new ideas. Interface Builder. Since this is announced as sort of an advanced session, I'm assuming you're comfortable with Interface Builder. What I want to do though is focus on part of Interface Builder that I think up until recently has been fairly underutilized.

Until recently, because if you went to the Quartz Composer session today or the QT Kit session the other day, you saw some great Interface Builder palettes that have been created. Also, if you look in /developer/extras/palettes on your DVD, you'll see a number of new palettes that have been created by teams outside of the Interface Builder team.

What that means is basically, well to some extent I didn't know that these other teams were working on these palettes, but more importantly it means that these other teams did this without the help of the Interface Builder team. In particular, they'll be using basically the same process that we're going to be talking about today. Again, if you have an opportunity to see or look at the slides from those previous two sessions or say one of the Cocoa binding sessions, you'll see some interesting new palettes that have been created. and as well as look on your disk.

So, Jon was going to introduce me, but I'm Chuck Fleming, new manager of the IB team. And again, what we want to talk about today is Interface Builder and how to extend it, make it more powerful, and extend it by creating your own palettes. So you should, I guess, first of all ask why would you want to write an Interface Builder palette? What does that do for you? And first of all, it gives you a very convenient way to get your objects in the hands of your developers.

[Transcript missing]

And the third reason, if you've gone to any of the Cocoa Bindings sessions, you know Cocoa Bindings was introduced last year. is very exciting technology that's been displayed this year as well, particular in the Quartz Composer session this morning.

Terrific use of bindings was shown in the setting of an Interface Builder palette. So, third reason why you might want to write a palette is to take full advantage of Cocoa bindings for your custom views within Interface Builder. The only way really to expose your bindings at this point is to implement a palette.

So, very quickly, topics we're hoping to cover. Number one, building and debugging palettes, sort of getting things going quickly. Step two, some additional palette tasks. We're going to talk about how to make things a little bit more fully functional, add some features that you would expect to have your inspectors fully working, maybe undo support, tool tips, things like that. The third and fourth topics, a little bit miscellaneous collection of topics here, some tips, not entirely related to palettes, but they touch on topics that you should know as you're writing palettes or playing with nibs in general. And then very quickly, I'll just give some performance suggestions.

Okay, so how do you make a palette? You use the two tools that, two of the tools Apple provides, Xcode. We are going to provide you with an enhanced template, project template for, specifically for making palettes. It's significantly improved over the one that's currently shipping. That's in the disk image that will be available for download at the end of this session.

And then Interface Builder itself, because you're going to assemble the palette graphically and also assemble the inspector graphically. And that's what Interface Builder is all about, assembling UIs graphically. So, the first thing, we're going to take the very simplest case. How many of you guys have built a palette before? Okay, it's smattering but not overwhelming, which is good, because even though this is advanced topics, I'm going to start with the real basic stuff and it's very easy to do. So hopefully I won't bore the advanced users here too much.

[Transcript missing]

The template lives in Home, Library, Application Support, Apple.

Developer Tools, Project Templates, Standard Apple Plugins. And here we have our IB Palette WWDC 2004 demo. So that's a long path, but it's also the place where the standard Xcode templates live. So if you're familiar with that, it's the same place except starting in your home directory. So I'd like to go ahead and introduce you guys to the WWDC 2004 template for building Interface Builder palettes.

So I'm going to go ahead and launch Xcode and create a new project. Now, since we have that template living in that folder, we'll be able to see it here in the standard Apple plugins. So I'm going to select the IB palette, WWDC 2004, and create a new project named Widget.

So, our new project here on the screen is going to include everything we need to do to get started building an Interface Builder palette. The most important thing to notice about the project is that it's been divided into two targets. We have a widget target and a framework target. The widget target is going to hold our Interface Builder palette, which is going to be everything we need to configure our object that we're exposing through interface graphically, while the widget framework is everything that an application needs to consume the widget at runtime.

So let's go ahead and take a look first at the widget framework. I'm going to go ahead and expose the target membership here so we can see exactly which files belong to which groups. So in our framework folder is all the files that by default belong to the widget framework. And here we have a view object.

Subclasses from NSView. So we're going to use this to provide a new view to users. And also, in our palette folder, we have two interesting classes of note. We have an inspector that we're going to use to configure the view graphically and a palette that's going to hold the view so that users can drag and drop it into their projects.

So with this, I'd like to give it back to Chuck to continue the presentation. Okay, can we get the slides again? Okay, so very quickly, what was generated for us? Three classes. The Widget class, which is the view that's going to appear on your palette. The Inspector, which is going to be the files owner for the Inspector nib file. And the Palette, which is sort of a controller object, it's also the files owner for the Palette nib that got created for us.

We'll look at those in detail in a second. So again, Widget, that's the view object that's going to go on the Palette, and by default it's a subclass of NSView. When we switch over to looking at non-view based Palette objects, you're going to have to change that yourself to make it whatever class you want it to be.

In Widget.m, again, that's the view that you're going to put on your Palette. It has, the template creates a skeleton implementation of three methods for you that you do need to fully implement. Anit with Frame, Anit with Coder, and Encode with Coder. Anit with Frame, you need because that gets called when your view first appears on the Palette in IB. And that gets loaded lazily, so it doesn't happen automatically.

It will happen, say, for example, the first time you click on the tab that represents your Palette in the Palette's window in IB. Anit with Frame will get called, so any initialization you'll do there. The Anit with Coder and Encode with Coder are needed because they're called when your view first appears on the Palette in IB.

So any initialization you'll do there. The Anit with Coder and Encode with Coder are needed because they're called when your view first appears on the Palette in IB. So any initialization you'll do there. needed because, for example, when you drag your widget off the pallet, nib, into your design window, we're going to archive that object and put it on the paste board, so encode with coder will get called there, and then we drop it in the design window, it'll get unarchived and so a nit with coder will get called. And finally, for example, when you save your nib file with the new object in it, that object will get freeze-dried again and unarchived and so encode with coder will get called. So you need to implement those three methods.

And again, since this is an advanced -- more advanced session, I'm assuming you guys know about coding and some of the standard AppKit and foundation techniques. What else was created? The widget palette. That's a subclass of IB palette. And basically the most important, the take-home point on this one is it should be the files owner of the widget palette dot nib.

In there again, skeleton implementations of a couple of methods. Finish Instantiate. This guy is primarily used when you have the non-view case, when you have an object that you're going to put on the palette, which isn't a subclass of NSView, but you need to associate a visual proxy for that object.

So that you can graphically manipulate it, even though it's not a view. Normally you do that association in Finish Instantiate, and we'll show you the teeny bit of code you need to make that happen. And then also the Inspector class name, by default a reasonable choice is given there. If that isn't what you need, then you can certainly modify that. The Inspector class is created for you. That's a subclass of IB Inspector. It's the attributes inspector for your widget. And... And again, it should be the files owner of the inspector nib.

Okay, again, here's the stub implementations that are created for you. Anit, OK and Revert. Anit is probably where you want to load the bundle that contains your inspector nib file. OK and Revert mirror each other. Revert is going to be called by IB and give you the opportunity to pull information from the inspected object and insert it into the UI, your inspector.

And OK does the opposite. When you trigger the OK action, you'll be responsible for pulling information out of the UI and pushing it into the object that you're being inspected. So these guys are trying to keep things in sync, and so in the back of your heads you should be thinking Cocoa bindings.

So this is the old style. It works fine. Think in terms of Cocoa bindings as you go forward, because this is a synchronization issue, basically. Let's see, what else? A couple of nibs. Jon's going to show us those in just a second. Basically, the palette is where you assemble what's going to appear in I.V. in its palette window. Files owner, you're going to have to change that, because we couldn't do that for you in the template.

and the Inspector Nib itself, that's where you're going to assemble the widgets that will get extracted and then put in the Inspector as Interface Builder is running. And again, the Files Owner, I'll mention this several times, Files Owner needs to be set appropriately. Alright, so Jon, I think you were going to just give us a quick look at those nib files.

Okay, so let's go ahead and try to make this sample project runnable with Interface Builder. And the first thing we're going to do is we're going to fix those files owners like Chuck mentioned. So I'm going to open up two of the files that I'm going to show you now, which are the widget palette nib in Interface Builder, and next we'll do the widget inspector. So in the widget palette, you'll see we have the files owner in one window. It's very important that the files owner has its custom class set to be a widget palette.

Also, the files owner needs to be connected to this palette view right here. And we can see it's connected. This palette view is what's going to be loaded into the Interface Builder palette section over here, and we'll be modifying that later. Let's also open up the Widget Inspector and make sure that its files owner is set to be an instance of Widget Inspector. Also, we need to make sure that the files owner is bound to the Widget Inspectors window. So now we have our nib set up, and we'll be ready to move on shortly.

Back to the slides. Okay, so that's sort of the minimal setup. And now we just wanted to add a tiny bit of code so you can see that, well, we have a functioning palette at this point. So Jon's going to do a real quick modification to the code, build it, load the palette, and see that it really is sort of functional. So back to the demo.

Okay, so we have our palette here, and on our palette we'd like to place a widget view. So what we're going to do to do this is put in an NSCustomView, and we're going to change the custom views custom class to be a widget. Now, at this point we have everything that's needed to get a widget loaded into Interface Builder, but unfortunately our widget doesn't paint itself, so we wouldn't be able to see it. So we're going to modify the widget skeleton that was generated for us by the template, and we're going to include a drawRect method.

So in the drawRect method, we just want to make it visible for now, so I'm going to go ahead and choose the blue color and go ahead and fill our clip rectangle with blue. Now from here we can build a project. And we want to go ahead and make sure it gets installed in the correct location. So I'm going to go to our home folder, move to Libraries, Frameworks, Framework. Going to Framework. Frameworks.

And here's our widget framework. And then also we go to Palettes, we'll see that we have our widget palette. So let's go ahead and go back to Interface Builder and let's go ahead and load up our widget palette. So in Interface Builder's preferences, its last tab is the Palette tab. We can choose to include our palette here.

Here's our widget palette. I'm going to select it and it's going to load up in Interface Builder. At this point, we can create a new and Nick Nib, and our widget is fully functional and ready to be drag and dropped into new applications. So that's all we have to do to get a view onto a palette. and also the inspector, it looks like you do Command-1? So we can see in the inspector that Interface Builder is aware that this inspector is inspecting a widget object, and if we were to have edited our inspector nib, the properties would appear there.

Okay, so I want to kind of move on to the non-view case, which is really not much different from the view case, just a slight modification. But before leaving the view example, I wanted to point out that on the shipping Panther and I'm not sure if it was earlier, but certainly Panther and Tiger, if you look at on your hard disk in /developer/examples/interfacebuilder, you'll find a nice fully featured sample project called Progress View Palette that illustrates in some more detail the process of getting a view-based object on a palette.

Okay, so again, heading toward the non-view-based object case. Before I get to there, what I want to do is just mention something that's probably obvious, but When you assemble an application and you're bringing things off the palette, say your palette objects into the application and building it, designing it, and then you save and build. At runtime, of course, your application needs to have access to the code for those palette objects. So clearly one way to do that would be you've got the source code, the .m and .h for your custom views. You could distribute those, but that would get pretty tedious.

Probably a better approach is to make sure that the classes for objects on your palette and associated objects that may be needed are wrapped up in a library, or sorry, a framework. And so one of the things our new palette does is it makes that job easy for you.

It's certainly customizable, but it will create the framework for you and install it in convenient location, as well as the same for the palette. As Jon showed. So what we do with the framework is it will end up installed in Tilda Library frameworks, and the palette gets installed in Tilda Library palettes.

Okay, so back to getting an object on a palette where that object may not be a view-based object or a subclass of NSView. So it requires a small change to the nibs and some small code modifications. So the template's going to make that easy. Jon's going to show us again in detail some of these things.

But what the palette project does for us, the new project, is it sets up three different targets, one for the framework, one for the palette, and an aggregate target. It sets up several new groups to organize your code in a reasonable way. Establishes build dependencies for you, exposes the headers that you might need. It creates some files to support undo, implements the-- or gives you a palette table in which you can store some other information like tool tips, things like that.

[Transcript missing]

requires the framework to build. This way, the random number generator only lives in one library, and Interface Builder is going to need both the palette and the actual random number generator in order to include those in your project, whereas application developers will only need your framework to use your random number generator. So you want to show the command? So we show that... We can see those dependencies here by looking at one of our targets. and the Inspector. And we can see that the random number palette requires the random number framework.

I also showed the dependencies on which files belong to which target, if you would. Okay, sure. So in the framework is all the, like I said, all the files that belong to the framework which applications are going to need. We can see these two files belong to that.

And if we look at the Interface Builder palette, we'll see that all the rest of the files belong to the Interface Builder palette because Interface Builder will need this code and these nibs in order to edit the object graphically in its runtime. And also, if you could disclose the undo support as well.

So we also provide-- Sorry, go ahead. So when an object is being edited in Interface Builder, we're going to take snapshots of that object when you tell us to, when we know that your inspector is about to edit the object. And these IB class description files expose the properties of the object that you want to have snapshotted at runtime.

So for example, for a random number generator, we're going to take a snapshot of the min value, the max value, and the frequency attributes, which are the instance variables of the random number generator. And so when the user presses undo, we'll be able to swap back the old values and swap in as the user modifies things and presses undo. So these classes are key to implementing undo support in Interface Builder.

Back to the slides, please. Okay, so as we mentioned earlier, you've got a non-view based object like a random number generator. You want it on the palette. You need to graphically manipulate it, so you need to assign a proxy view to it. Very simple process. You're going to instantiate the object, and then you're going to associate a view with that object. So Jon's going to quickly show us where we do that in the code.

So it's interesting to think of how we can put a non-view-based object into an Interface Builder or a palette, since a palette contains view objects. So we go ahead and look at our random number palette, which I'm opening Interface Builder now. We see that we have this R here representing a random number generator.

This is what we want the user to see in the Interface Builder palette when they're using our palette that we're generating for them. However, if we inspect this, we'll see this in NSImageView, not a random number generator. So what's going on behind the scenes is in our palette class's FinishExtantiate method, we're telling our base class to associate a random number object with that image view.

So our base class, which is the IB palette, is going to know that when the user drags one of these images, that when he deposits the image onto his object browser, he really means to deposit a random number generator and not the NSImageView that he initially dragged. So that's how we're able to put non-view-based objects into palettes and have the IB runtime manage that for us.

Okay, so before going any farther, there is a very nice example, again, that shipped with Panther, available on Tiger, that you might want to take a look at that shows you, in addition to the tips and tricks we'll show you today, some additional features that we won't have time to get into. For example, supporting drag and drop onto your objects.

That, in fact, gives you a nice example where you might need something done before the Instantiate object, which is where we just did that mapping between the view object and the non-view object, or the view proxy and the non-view object. Sometimes you need to do stuff before finish Instantiate, and one of the things is for drag-and-drop support. So you'll implement your init method and do that there. A place to look for some other very good tips is /developer/examples/interface-builder and look at the Busy Palette example.

Okay, so at this point we have the bare minimal functionality and we want to go on then and show what's involved in making your inspector fully functional. I'm sure you have a pretty good idea of what we need to do. Supporting undo and redo, which hopefully Jon's given us an indication that that's pretty trivial.

And then what about working with Cocoa bindings? Okay, so we've already seen putting an object onto a palette twice. Now let's go ahead and take a look and see how the inspectors work. I'm going to go ahead and open the inspector nib that was generated for us and we had modified.

and in the inspector you'll see that we have properties reflecting what was in the undo support. We have a text field for setting the maximum and minimum value along with the frequency of a random number generator. We'll see that each of these has an action that's bound to the file's owner.

[Transcript missing]

and I will be talking about the previous project and make sure that it gets installed in the correct location. Quickly we can note while it's building that in the random number inspector, In the OK and revert methods, where we're pushing the properties from the view to the object and from the object to the view, we can see that in the OK method, we're telling Interface Builder that the object will change.

This is the point when Interface Builder is going to use that IB class description that you generated to take a snapshot of your object for unarchiving in the future to implement undo. So now that our project's built, let's go make sure it's in the correct location, which again is in your library frameworks folder.

There's our random number generator. And let's also make sure we've got the palette. And our palette. So back in Interface Builder, we can go to the Preferences. We can choose Palettes. We can press Add and select our random number palette. So in the Random Number Palette, let's go ahead and actually utilize this. In Xcode, I'm going to create a new project.

We'll make a sample. We'll use the Cocoa template for an application. Let's call it RandomHarness. So, since our Random Harness is going to want to use our Random Number Generator, we need to import the framework that we generated with the Random Number Project. So we're going to add that to our project.

[Transcript missing]

We're going to grab Random Number Framework because we're going to depend on that. And let's go ahead and put a random number generator into our nib file. So in our resources, we're going to go to the main menu nib.

Okay, let's just start that back up. So in our main menu, oh, we gotta grab the, uh, the random number generator again. So here we go. And in our nib that we just generated, we're going to grab one of these random numbers. And like we said, we associated a random number generator with this NSImageView.

And when we drop it, we're going to get a random number generator. So to utilize this, let's go ahead and put some graphical elements on our window to expose what's going on with the random number generator. I'm going to put a text field here and two buttons. Let's put a Start button and a Stop button.

and let's go ahead and configure our random number generator with the inspector that was shown earlier. So we'll say it will have a min value between 0 and 100 and let's go ahead and make it fast. So we'll set the frequency to 0.2. So if we set the stop button's action to be stop on the random number generator and do the equivalent for start, And we're going to go ahead and use Cocoa Bindings to tell the text field to bind to the value that's exported by the random number generator. So tell the text field to get its value.

Now it looks like we have a bug. Yeah, we should be able to see random number generator here and choose random number here. Are we running Tiger? I don't think so, no. That's probably our problem. That is our problem. So-- So the latest release of Interface Builder, you can choose other objects besides NSControllers to bound to for Cocoa bindings.

So when you go ahead and use your latest copy of Tiger that you were provided with, you'll be able to bind to this random number, this random number ibar for your text field to display the values automatically. But with the older version, we're not allowed to do that.

So that's, yeah, should have been kind of surprising that there's no object controller here. So we've added preliminary support so that you can bind directly to model objects and not just controllers. So unfortunately, though, it looks like we're running a version of Panther here. So, okay. Let's go back to the slides.

Okay, so what did we just do? Well, we really didn't do anything. We had this all pre-canned. But basically the steps that you would need to do, as Jon pointed out, you've got the inspector, you're going to drag some text fields or graphical widgets in there. You'll take the inspector, which is the files owner, and hook up and set up outlets to point to those widgets, so that when the revert method gets called, I should say when the revert message gets sent, otherwise Jon will scold me.

So when that message gets sent, the revert message, that we can get to those objects in the inspector and pull the values out and insert them into the inspected object. And then you also want to set up your actions, your OK actions, so that when, say, you do something in the inspector, you trigger an action, which will then get sent to your inspector object. And the result is your inspector object then wants to take the values from the UI and push those into the inspected object. And that's basically what you'll need to do.

OK, undo support. It's very easy. You make a slight change to your OK method and Add an IB class description file. Now, the template that we provided has done that for you. In fact, Jon actually showed us this as he was looking at the inspector implementation. But the highlighted text shows you how basically to support undo. You'll want to call that second line or have that second line implemented for every object that happens to get changed when you're doing the inspection.

And then the class description file, again, you just list the instance variables, the names of the instance variables that you want to participate in undo. If you have a class that has a bunch of IVARs and only a small subset, then obviously you want to-- a little performance enhancement is just to list the ones that need to participate.

Inspectors and Cocoa Bindings. So as I mentioned earlier, we've got this OK and revert method thing going on that keep things in sync. And that's precisely what Cocoa Bindings is good at doing for you without writing those methods, implementing those methods. So it works very well on Tiger.

Non-view based objects. Interface Builder has been around for a long time. It's much earlier than Cocoa bindings. So that means there are some possible edge cases when you're working with view based objects. In particular, we do some caching of the images that might make you work a little bit harder to get things to refresh when you implement undo and redo. But basically it should work. There might be a few edge cases, so you might have to do a little tweaking.

So I think, Jon, you were just going to show us the example, what's involved, what's left in your project after you use the Cocoa Bindings approach for an inspector. So, like Chuck said, bindings are a great thing to use in inspectors because all you have is a bunch of get set mappings, keeping the objects in sync.

So here, instead of having an OK and revert method in this slightly different instance of the random number generator project, instead we just have set value. We're overwriting the key value coding for our class to push the

[Transcript missing]

Okay, so normally you see the people bring up the project with the file merge, so you know. The okay and revert methods are now gone. There were no instance variables left, and you're down to a minimal amount of code.

Okay, back to slides. Okay, just a few additional details. Where does IB look for palettes? Where should you put the palettes? It looks in a bunch of places, but the places we recommend: Tilda Library Palettes, Tilda Developer Palettes. Classes that you want to expose, you know, the main class that you've put on the palette plus maybe some auxiliary classes, make sure those are mentioned in the palette table file.

Tool tips: When you're in the palette window in IB itself and you're mousing over objects in your palette, you can specify tool tips. You do that also in your palette table. One thing I didn't mention on the slide is the image that represents your objects on the palette that you click on. You can specify that in the palette.table file.

Okay, very quickly I just want to mention a couple approaches to debugging your palettes. And then a couple of topics that may be useful while you're debugging palettes, but also may be useful in general. to let you modify how things look depending on whether you're running in IB or whether or not you're running in IB's test mode. So, first of all, debugging. Well, if you're from the old Unix world, then your old friends, you know, Terminal, Emacs, GDB still work great.

Okay, so that's always a fallback. But if you want to do it the Xcode way, Xcode now provides a very nice way to debug your palettes. You've got your palette project you're working in. I, sorry, Xcode has a facility for assigning a custom executable to your project. And basically the custom executable you want is Interface Builder.

So when you launch the application, your custom application, it'll be Interface Builder. You're in your palette project, you set breakpoints in the appropriate places or reasonable places, for example, in your inspector class name, finish instantiate, okay, revert. Any, any, any reasonable places you want to see. Is this code being executed or not? If something's going wrong, why? Then simply start GDB in Xcode. Your, and, and load the palette.

So that gives you a nice graphical environment in which to actually debug your palettes. Okay, the other two questions: Can you determine if you're actually running in IB or not? Well, you can just basically ask, does your widget class, for example, respond as selector is in Interface Builder? IB defines that in a category on NSObject, so you can simply see if, and the implementation is highlighted here. So you can just test, do your respond to that.

If you're in Interface Builder, but maybe you want to know whether you're in test mode or not, maybe you want to do some drawing differently so you can see what state you're in, you can simply ask NSApplication, "Are you in test interface mode?" That's a method that's made available through IB Application Editions, which is a public header.

Okay, so I'm going to switch gears just a little bit now and talk about some things that are perhaps not entirely related to palettes, but good things to know. They do help you if you understand, so in your understanding of how palettes work, good things to keep in mind, but also for nib loading in general. And some of these, we see a lot of questions on the mailing list, especially from beginning palette programmers, about what methods get called, messages get sent, and when. In particular, init or initWithFrame, depending whether you're a view or non-view object, awakeFromNib, initWithCoder, and so on.

So, the first case is you've just clicked on your palette in the palette window in IB. What happens first? Well, that's where your NetWithFrame will get called, on your View class. Let's take the example again. This is a view-based object. So a knit with frame gets called there.

Then the next thing, you drag that view into your design window. Again, it gets archived when it gets put on the pasteboard, and when you drop it, it gets unarchived. So a knit with coder gets called first, and then-- or sorry, encode with coder when we archive it, and then a knit with coder when we drop it. Then you save the nib.

And then in your running application that nib gets loaded. So what happens when the nib gets loaded this freeze-dried object, this freeze-dried view that you had in your nib file gets unarchived. So a knit with coder gets called. Some folks expect, oh this is a view, my knit with frame will get called. But no, it's the knit with coder that gets called because again, it's a freeze-dry, we're thawing this object when the application runs. So this slide just basically summarizes what we saw in the previous slides.

So, init with coder. The second slide, I think, is a little bit more important. The init with coder is going to get called, not your init with frame. And then, of course, after that, awake from nib will get called. A couple of caveats to keep in mind. You can't count on your connections having been formed in the init with coder. You have to wait until awake from nib to make sure all your connections are formed.

So, again, this was a case of a view. In the case of a non-view object on the palette, instead of... Basically, everything is the same except init with frame gets called... Or, sorry, init gets called instead of init with frame. And it's important to note, if your non-view object has an initializer other than init, that's not going to get called. So, this only works well if your view, init with frame, or if your non-view, it needs an init to work in an IB palette. Here's another case.

Suppose this time you take a custom view, which is on one of the system provided palettes, and you drop that into your design window. But you want to make that view a special subclass, some subclass that you've provided. Either you've dragged in a header, or you pick another subclass of view that's available in IB itself. So you have a new view here now called My View.

So what happens when that gets saved? Well, the custom view is the thing that actually gets encoded. And it keeps track of the fact that, "Oh, by the way, I really wanted my subclass of view to be used at runtime." So the next step, say at runtime when that nib gets loaded, the custom view is the thing that gets unarchived.

and then it realizes, oh, we really didn't want a custom, we didn't really want the custom view, we wanted that subclass of view. So, basically the custom view kills itself off and allows for your subclass of view to get created. So in this case, a knit with frame does get called. Your subclass didn't get unarchived. It actually got created the normal way. So in this case, a knit with frame gets called.

and then that previous slide was just a summary of what we saw on screen. Let me skip that for save us some time here. The final and probably the most confusing case that we run into is suppose you drag off a system view, not a custom view, not a palette, but some sort of system view from IB palettes. A typical case might be an NS button. Even more typical, a lot of people take OpenGL view, which we provide on the palette, and you drag that off into your design window. And then you want to change what class it is.

So instead of say NS button, you've got your subclass of button, and so you use the classes tab in IB or the class inspector and you switch what class it is. So again, you might have a subclass of Button or a subclass of OpenGL View. So again, there's two ways you can come up with a subclass of OpenGL View. You could bring a custom view in and change it, or you could bring in an OpenGL View in and then change it.

So we're in the second case where we've taken a system view and we're changing its class. What happens in that case? Well, this is a little bit strange, but what happens is the NSButton, in this case, gets archived. So its encodeWithCoder gets called. When we save the nib, and then when the nib gets loaded at runtime, Well, something gets unarchived. In fact, the initWithCoder to your subclass gets called, even though what got encoded was the NS button.

So you have to be careful. If you do implement some encode with coder on your subclass, that's not going to get called. And furthermore, you probably shouldn't be pulling out, especially for TypeStream, the old TypeStream version of archiving, you don't want to be pulling things out of the stream, assuming that those are things that you put in there for your subclass, because it's actually the superclass that got encoded. So the behavior is a little bit strange there.

Because of that strangeness, you should be asking, "Well, why would you ever want to do that? Why would you want to bring in a button and then change its class?" Well, one answer is, say, a button or some widget that's a system provided may have a lot of attributes and state that needs to be set, and that state can easily be set in the inspector for that object. So you can take advantage of setting up all of that state by using the inspector. that you inherit.

Alright, so it looks like we're in pretty good shape for time. The final topics that I want to talk about basically are related to performance and some tips on designing your NIBs. So, for example, one way to get some nice performance in certain cases is to use NSNIB, which is reasonably new.

If you use NSNIB, you can get enhanced speed improvements at possibly a cost of memory. You get to load the Nib from the disk just one time. And then once it's in memory, you can instantiate it as often as you want very quickly. And again, the tradeoff is between loading speed and the memory footprint.

If you use NSNIB, you can get enhanced speed improvements at possibly a cost of memory. You get to load the Nib from the disk just one time. And then once it's in memory, you can instantiate it as often as you want very quickly. And again, the tradeoff is between loading speed and the memory footprint. Which basically, if I haven't already allocated the NSNIB, I'm going to go ahead and call alloc init with nib name: bundle: That's going to load it from the disk, but it doesn't instantiate. The objects are not created, they're not in memory.

And then, the next line is how you actually instantiate the nib. And then the final line is say the nib had some window in it so you could do make key-and-order front, so you see it come on screen. So a possible use for something like this is say iChat, where you have incoming requests from people to start a conversation. You might want to get those chat windows up as fast as possible. You might not want to hit the disk. Something like that.

A couple of pointers to some documentation on loading resources. Okay, for some more details. Some nib content strategies that are related to this. Nibs are fairly cheap. If you load them into memory, you can get them on screen fast. If you load them from disk, as long as they're not too heavyweight, that shouldn't be a problem. But remember that every object in your nib is going to get instantiated.

So, excuse me, so they're cheap as long as they're skinny. Don't, try not to put a bunch of Windows in your Nib files. Don't put a lot of objects that perhaps could be lazily loaded, stored in other Nibs and loaded, or other bundles and loaded when needed. Okay, and the other thing when you're finished with a nib file, make sure you free the top level objects. Okay, and again some more details on where you can get information on those topics. Slides will be available.