Tuesday, October 20, 2009

Loading an auxiliary window's nib file



This post is about loading the nib for a new window. It follows up on this one, this one, and this one.

As I described last time, loading a nib file and seeing the window appear requires a single line of code. But the question of communication between the objects in the mainMenu.xib and the auxiliary window had me scratching my head. Google brought me comments like "just hook up outlets to File's Owner" [in the auxiliary nib] but you can't hook up outlets without a header file to declare them, and File's Owner doesn't have one. I could use an NSNotification, but there's an official way to do it.

I turned back to Hillegass. I did this almost exactly as he does, so I'm not sure which of the added elements is essential yet, but it's a working example.

Step 1

He uses a subclass of NSWindowController, so I added that to my working version of the auxiliary window project. I added a cube for the ColorWindowController in the nib, wrote the class files, and hooked up outlets as you'd expect from this header. Everybody is an outlet for the window controller.


#import <Cocoa/Cocoa.h>
@class MyColorView;
@class MyTouchableView;
@class PrefsController;

@interface ColorWindowController : NSWindowController {
IBOutlet MyColorView *myView;
IBOutlet MyTouchableView *myTView;
IBOutlet PrefsController *myPrefsController;
}
@end


I've basically just moved the outlets out of the AppDelegate. No big deal. I test the outlets in the interface for ColorWindowController.m. Is the window controller important? I don't know yet. I used the @class forward declaration just like he did, for no good reason. We could import the headers instead.

Step 2

I made a new project called WindowLoader. Once again, rather than put any code in the AppDelegate I added a cube for a new object to mainMenu.nib, and named it AppController, wrote the files as usual, and hooked it up as the target of a button in what will be the main window. A bare skeleton of a project.

Step 3

Copying files is pretty much as you'd expect. I pasted in all 8 files (4 classes and their headers) from the working auxiliary window project to the new WindowLoader, plus I renamed the auxiliary nib to be ColorWindowMenu.xib and then copied it, and made sure all the files are in the correct folders as shown in XCode.

Step 4

Now it gets interesting. I opened ColorWindowMenu.xib and set the class for File's Owner to be AppController. The object we instantiate as part of the mainMenu.xib stuff is set as the owner of the auxiliary window. In the code for AppController we do:


#import "AppController.h"
#import "ColorWindowController.h"
@implementation AppController

- (IBAction) showPanel:(id)sender {
if (!cwc) {
cwc = [[ColorWindowController alloc] init];
}
NSLog(@" showing %@", cwc);
[cwc showWindow:self];
}
@end


Instantiate the ColorWindowController and then do showWindow. This is the step that makes me think it's important for this class to be descended from NSWindowController. :) Anyway, even if it's not strictly necessary, it would seem a bit perverse to not use a window controller to um, control a window.

Step 5
There is one final step. In ColorWindowController.m we add an init method and a test to show everything is hooked up. It works!


#import "ColorWindowController.h"

@implementation ColorWindowController

- (id)init {
if (![super initWithWindowNibName:@"ColorWindowMenu"]){
return nil;
}
return self;
}

- (void)windowDidLoad {
NSLog(@"CWC self %@", self);
NSLog(@"CWC myView %@", myView);
NSLog(@"CWC myTView %@", myTView);
NSLog(@"CWC myPrefsController %@",
myPrefsController);
}

@end