Thursday, March 28, 2002

Brent Simmons asks But, what do you do when you want a variable number of instances for a given window or set of windows? in response to an excellent article at Cocoa Dev Central regarding loading of multiple NIB files.

The key-- as usual-- is to remember that there is no magic when developing Cocoa applications.

Typically, you load a NIB file by invoking something like [NSBundle loadNibNamed:"MyDocument" owner:self]. When the NIB file is loaded, all objects inside the NIB are instantiated except the File's Owner. The File's Owner is the object passed as the second argument to the -loadNibNamed:owner: method. In the aforementioned example, self would be the File's Owner.

Once all the objects are instantiated, all of the connections are made. That is, if you have a Window object and you connected it to some other random object via the delegate outlet, then the delegate instance variable of the Window will point to that other random object when the NIB is loaded and connections are made.

Same goes for File's Owner. If your instance-- the one referenced by self-- contains a declaration for an instance variable (or outlet in terms of IB) named myTextField and you connected that outlet to a text field in Interface Builder, then the connection will be made when the NIB is loaded. That is, after the call to -loadNibNamed:owner:, the instance variabel myTextField will be a reference to whatever you connected it to in the NIB file.

There is no magic.

Say you were to call [NSBundle loadNibNamed:"MyDocument" owner:self]. All of the connections from self to other objects would be overwritten upon the second load; that is, myTextField would now point to a new instance of TextField that resides in the freshly loaded window.

A useless example.

A more useful example: You can define a bunch of outlets and action methods such that some are connected in one NIB file and others in a different NIB file. Say you have some kind of a document that has a SIMPLE view and a COMPLEX inspector. In your class, you define:

@interface MySimpleComplexController: NSObject
	id window;
	// simple UI
	id nameField;
	id descriptionField;
	// complex UI
	id widthField;
	id heightField;

You could then construct two NIB files; a simple one that only has a name and description and a more complex interface that includes name, description, width and height.

In both NIB files, set File's Owner to be an instance of MySimpleComplexController.

Then, in Simple.NIB, drag connections from nameField and descriptionField to the appropriate fields in the UI. Likewise, in Complex.nib connect all four outlets-- name, description, width, height-- to the appropriate UI elements.

You could then load the nibs via something like:

- (void) updateUIFromObject
	[nameField setStringValue: ...];
	[descriptionField setStringValue: ...];
	if (widthField) // not necessary, but I like it
		[widthField setStringValue: ...];
	if (heightField)
		[heightField setStringValue: ...];

- (void) loadNIB { NSString *nibName; // if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UserWantsComplexInspector"]) nibName = @"Complex"; else nibName = @"Simple"; // [NSBundle loadNibNamed:nibName owner:self]; // [self updateUIFromObject]; [window makeKeyAndOrderFront: self]; }

Now, say you wanted to have multiple simple or complex inspector windows on screen at the same time.


In whatever controls the inventory of things to inspect, you would do:

- (void) displayWindowForThing: aThing
	MySimpleComplexController *myInspector;
	// assumes you have created an -initWithThing: initializer on MySimpleComplexController
	myInspector = [[MySimpleComplexController alloc] initWithThing: aThing];
	[myInspector loadNIB];

That is, you create a separate File's Owner for each instance of MySimpleComplexController you want to display. This is how the whole multiple document architecture works in Cocoa (in theory, the actual implementation of MDA is much, much better than this example).
3:43:43 PM  pontificate    

I hate it when I'm about 4k into a long winded email explaining something and my computer loses power....
10:46:36 AM  pontificate