xcodemacoscocoaxibnssplitviewcontroller

NSSplitViewController/NSSplitViewItem support in XIBs


Is there support for NSSplitViewController/NSSplitViewItem for XIBs? I see only NSSplitView

Can I just drag&drop NSViewController and subclass it as NSSplitViewController? How do I add NSSplitViewItem that it mostly works out of the box?

Split View Controller in XIB

I can easily see support for them in storyboards.

Split View Controller in Storyboard


Solution

  • Yes it's possible. But it needs some wiring.

    Result

    First add a custom subclass of NSSplitViewItem and expose viewController property as IBOutlet. Compiler will throw a warning so don't forget to mark property as dynamic.

    @interface MySplitViewItem : NSSplitViewItem
    @property  IBOutlet NSViewController *viewController;
    @end
    
    @implementation MySplitViewItem
    @dynamic viewController;
    @end
    

    In your XIB add 3 NSViewController objects. One of them change to custom class NSSplitViewController. It is important to note that one should NOT add NSSplitView. Wire NSViewControllers to it's views. Also add 2 objects and add custom class of MySplitViewItem which has exposed the viewController and wire it.

    XIB

    Last step. It is important to set property splitItems of NSSplitViewController before the views are loaded! Otherwise you are caught with NSAssert macro.

    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
    {
        NSNib *nib = [[NSNib alloc] initWithNibNamed:@"Empty" bundle:nil];
        NSMutableArray *test = [NSMutableArray new];
        NSMutableArray *splitItems = [NSMutableArray new];
        NSSplitViewController *controller;
        [nib instantiateWithOwner:self topLevelObjects:&test];
        for (id object in test) {
            if ([object isKindOfClass:[NSSplitViewController class]]) {
                controller = object;
            }
            if ([object isKindOfClass:[NSSplitViewItem class]]) {
                [splitItems addObject:object];
            }
        }
        [controller setValue:splitItems forKey:@"splitViewItems"];
        [[self window] setContentViewController:controller];
    }
    

    Here is a proof that everything is wired correctly. Note that I did not touch delegate in XIB and it is wired. Magic, I know. Result

    PS: XIB has to be set to prefer Coder + auto layout.

    Why do I prefer XIB? Because we can create larger XIB which doesn't suffer from data isolation (Easily can do bindings across NSViewControllers).

    I have also experimented to add splitViewItems in viewDidLoad or setView or awakeFromNib: in custom subclass of NSSplitViewController (with exposed NSSplitViewItem properties). If someone finds solution here it will be greatly appreciated.

    Solution that requires code only:

    - (NSSplitViewController *)profilesSVC
    {
        if (!_profilesSVC) {
            NSSplitViewController *splitVC = [[NSSplitViewController alloc] init];
            ProfilesViewController *profilesVC = [[ProfilesViewController alloc] initWithNibName:@"Profiles" bundle:nil];
            NSSplitViewItem *leftItem = [NSSplitViewItem splitViewItemWithViewController:profilesVC];
            [splitVC addSplitViewItem:leftItem];
            ProfileViewController *profileVC = [[ProfileViewController alloc] initWithNibName:@"Profile" bundle:nil];
            NSSplitViewItem *rightItem = [NSSplitViewItem splitViewItemWithViewController:profileVC];
            [splitVC addSplitViewItem:rightItem];
            _profilesSVC = splitVC;
        }
        return _profilesSVC;
    }