I came up with a possible solution. If may not be an exact fit for you, but here's what I did in coding a sample project where the window doesn't need to know the classes of the panel view controllers.
I created a class which I called PanelNibFileOwner. All it has is an outlet to a view controller. The outlet is an assign property called viewController. PanelNibFileOwner's dealloc calls release on it's viewController. The only purpose of this class is to be the file owner and to receive a reference to view controller in the panel nib when it's loaded. This technique saves us from having to look for the view controller amongst the top-level objects.
Each panel is a nib called "Panel1.nib", "Panel2.nib", "Panel3.nib" etc. I'll use this naming convention to auto-discover and auto-order the panel nibs.
For each panel, I created a view nib file. I set class of the File's Owner to PanelNibFileOwner. I added a View Controller to the nib and set the class of the view controller to the specific subclass for the panel. I connected the view controller to File Owner's viewController outlet. I connected the view to the the view controller's view outlet. I also set the view controller's title in the nib. This will be used later in the nib loading code. Finally I set the autosizing properties of the view to anchor on all edges and grow in both directions.
I created a window controller for the panel window and did the panel loading and management in that, but you could equality do it an app delegate if you wanted to.
I put a popup button in the window and connected it to a panelSelector outlet in my window controller. I also put a custom view in the window and connected it to a panelContainer outlet. The panels will be shown inside this panelContainer.
I also added a currentViewController ivar to the window controller.
I added this discoverPanels method to the window controller.
Code:
- (void)loadPanels
{
[self.panelSelector removeAllItems];
NSBundle *mainBundle = [NSBundle mainBundle];
NSArray *nibURLs = [mainBundle URLsForResourcesWithExtension:@"nib" subdirectory:@""];
for (NSURL *url in nibURLs) {
NSString *lastPathComponent = [url lastPathComponent];
if ([lastPathComponent hasPrefix:@"Panel"]) {
NSLog(@"%s Loading %@", __PRETTY_FUNCTION__, lastPathComponent);
NSNib *nib = [[NSNib alloc] initWithNibNamed:lastPathComponent bundle:mainBundle];
PanelNibFileOwner *panelNibFileOwner = [[PanelNibFileOwner alloc] init];
BOOL success = [nib instantiateNibWithOwner:panelNibFileOwner topLevelObjects:nil];
if (success) {
NSViewController *viewController = [panelNibFileOwner viewController];
NSString *title = [viewController title];
[self.panelSelector addItemWithTitle:title];
NSMenuItem *menuItem = [self.panelSelector lastItem];
[menuItem setRepresentedObject:viewController];
}
else {
NSLog(@"%s Failed to instantiate %@", __PRETTY_FUNCTION__, lastPathComponent);
}
[panelNibFileOwner release];
[nib release];
}
}
}
I also added this selectPanel action, which I also connected the pop up button.
Code:
- (IBAction)selectPanel:(id)aSender
{
if (currentViewController) {
NSView *oldView = [currentViewController view];
[oldView removeFromSuperview];
}
NSMenuItem *selectedItem = [self.panelSelector selectedItem];
currentViewController = [selectedItem representedObject];
NSView *view = [currentViewController view];
[view setFrame:[self.panelContainer bounds]];
[self.panelContainer addSubview:view];
}
Note that I'm relying on NSMenuItem's representedObject to carry the view controllers loaded from the panel nibs and to associate then with the items in the pop up menu. If you don't use a pop up menu, you'll need some other technique for carrying and associating the panel view controllers.
Lastly I put this in my window controller's awakeFromNib
Code:
- (void)awakeFromNib
{
[self loadPanels];
[self selectPanel:self];
}
Hopefully you can adapt this to your needs. Note that there might be also sorts of memory leaks here, I've not checked for it.