Thursday, September 17, 2009

How to Create, Load, and Use Multiple Nib Files

In Mac OS X, application management is a single drag & drop operation: drop the application into /Applications to install, and drop it into the Trash to uninstall. This simplicity and power is just the tip of the iceberg. What we're seeing is just one use of a more general facility Cocoa calls bundles.

This article is about Nib files, and how to use them in your applications. Nib files are just specialized bundles, so we'll talk about bundles first.

// Bundles

A bundle is a directory containing code and resources. Applications, frameworks, Interface Builder (IB) Interface Palettes, and Nibs are all bundle types. The NSBundle Foundation class, along with the NSBundle Additions found in Cocoa, allow you to easily access and load a bundle's contents.

Getting a reference for your application's NSBundle is easy:

NSBundle * b = [NSBundle mainBundle];

Getting a reference for a given bundle inside your application is equally easy:

NSBundle * c = [NSBundle bundleWithPath:
[b pathForResource:@"TestBundle" ofType:@"bundle"]];

pathForResource:ofType: finds a given resource inside the receiving NSBundle's bundle directory, whether that resource is represented on disk as a file or directory.

Also, you can load a bundle's Principal Class, which is typically the first class listed in Project Builder(PB)'s "Files" tab for bundles. Say I had a class called BundleClass, and I ran the following code (after running all the code above):

bundleClass = NSAllocateObject([c principalClass], 0, NSDefaultMallocZone());
NSLog(@"loaded class %@", bundleClass);
[bundleClass init];

The output of my program would have been:

2002-03-23 15:29:20.637 Test[4709]  loaded class 

Suffice it to say, NSBundle is cool, and is the basis for applications, frameworks, Nibs, and a few other types of packaged data. There are plenty of useful methods for dealing with NSBundles, so do yourself a favor and check out the documentation.

// Nibs: A Special Type of Bundle

Now we come to the meat of this article: Nib files. Nib files are bundles that contain resources created by Interface Builder (IB). When you create a new Cocoa project, one Nib, MainMenu.nib, is automatically created for you. In MainMenu.nib, you can setup a menu bar (duh) and any additional windows, panels, or drawers you want.

Nibs contain archived instances of all the objects you built in IB. This includes instances of NSMenu, NSWindow, and any of your own classes you instanciate in IB. Once the Nib is loaded, every instance archived within it is unarchived into a running instance.

But, what do you do when you want a variable number of instances for a given window or set of windows? For example, for the document window in MDI applications like TextEdit? This is where we have to use more than one Nib file.

// Making a New Nib

Using a Nib is simple, in fact we all do it every time we make a new Cocoa application. Using more than one Nib is almost as simple: just create one with your original application in Project Builder (PB). Choose "Cocoa Document-based Application" under File..New Project. Under the "Resources" group in your newly-created project, you'll see MyDocument.nib. MyDocument.nib is a Nib loaded for each individual document opened in the application.

The other way to add a Nib is just to create a new one in Interface Builder (IB) and add it to your project. In IB, just hit File -> New -> Cocoa -> Empty, save it, and add it to your project.

// Multiple Client Windows Made Easy

Now that you've got a bright shiny new Nib, it would probably help to be able to do something with it. But how do you get references to all the stuff inside the Nib once you load it? In your new Nib, you'll see an object instance called "File Owner." This instance is there to hold a set of references for the newly created objects unarchived from the Nib you just loaded. You can add outlets and actions to it like any other object.

So, build your Nib like any other, and then connect everything you need externally accessible to File Owner. You can also set File Owner's class to anything you want. You can leave it as NSObject, set it to NSDocument like in the MyDocument.nib created in Project Builder (PB), or make it your own class. It's infinitely flexible, go have some fun with it.

So, all this talk and still no code? Here's how you load the Nib into your application:

- (void) newDocument:(id) sender {
MyDocument *d = [[MyDocument alloc] init];
if (![NSBundle loadNibNamed:@"MyDocument" owner:d]) {
NSLog(@"Error loading Nib for document!");
} else {
[d doSomething];
[_list_of_open_documents addObject:d];

Ok, so only the single call to loadNibNamed:owner: is necessary, but error checking never hurt nobody.

The code above fills in d's outlets with those specified in the Nib. d becomes the File Owner, and so it takes on all the File Owner's connections in the Nib.


0 коммент.:

Post a Comment

Powered by Blogger.