NSViewController and multiple subviews from a Nib

jbrennan picture jbrennan · Nov 13, 2009 · Viewed 18.4k times · Source

I'm having a difficult time wrapping my head around loading views with Interface Builder and NSViewController.

My goal is to have a view which meets the following description: Top bar at the top (like a toolbar but not exactly) which spans the entire width of the view, and a second "content view" below. This composite view is owned by my NSViewController subclass.

It made sense to use Interface Builder for this. I have created a view nib, and added to it two subviews, laid them out properly (with the top bar and the content view). I've set File's Owner to be MyViewController, and connected outlets and such.

The views I wish to load in (the bar and the content) are also in their own nibs (this might be what's tripping me up) and those nibs have their Custom Class set to the respective NSView subclass where applicable. I'm not sure what to set as their File's Owner (I'm guessing MyController as it should be their owner).

Alas, when I init an instance of MyViewController none of my nibs actually display. I've added it to my Window's contentView properly (I've checked otherwise), and actually, things sort of load. That is, awakeFromNib gets sent to the bar view, but it does not display in the window. I think I've definitely got some wires crossed somewhere. Perhaps someone could lend a hand to relieve some of my frustration?

EDIT some code to show what I'm doing

The controller is loaded when my application finishes launching, from the app delegate:

MyController *controller = [[MyController alloc] initWithNibName:@"MyController" bundle:nil];
[window setContentView:[controller view]];

And then in my initWithNibName I don't do anything but call to super for now.

Answer

Brian Webster picture Brian Webster · Nov 13, 2009

When breaking out each view into its own nib and using NSViewController, the typical way of handling things is to create an NSViewController subclass for each of your nibs. The File's Owner for each respective nib file would then be set to that NSViewController subclass, and you would hook up the view outlet to your custom view in the nib. Then, in the view controller that controls the main window content view, you instantiate an instance of each NSViewController subclass, then add that controller's view to your window.

A quick bit of code - in this code, I'm calling the main content view controller MainViewController, the controller for the "toolbar" is TopViewController, and the rest of the content is ContentViewController

//MainViewController.h
@interface MainViewController : NSViewController
{
    //These would just be custom views included in the main nib file that serve
    //as placeholders for where to insert the views coming from other nibs
    IBOutlet NSView* topView;
    IBOutlet NSView* contentView;
    TopViewController* topViewController;
    ContentViewController* contentViewController;
}

@end

//MainViewController.m
@implementation MainViewController

//loadView is declared in NSViewController, but awakeFromNib would work also
//this is preferred to doing things in initWithNibName:bundle: because
//views are loaded lazily, so you don't need to go loading the other nibs
//until your own nib has actually been loaded.
- (void)loadView
{
    [super loadView];
    topViewController = [[TopViewController alloc] initWithNibName:@"TopView" bundle:nil];
    [[topViewController view] setFrame:[topView frame]];
    [[self view] replaceSubview:topView with:[topViewController view]];
    contentViewController = [[ContentViewController alloc] initWithNibName:@"ContentView" bundle:nil];
    [[contentViewController view] setFrame:[contentView frame]];
    [[self view] replaceSubview:contentView with:[contentViewController view]];
}

@end