Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set custom NSSplitView with NSSplitViewController?

I'd like to use a custom NSSplitView with my NSSplitViewController.

The docs say:

To provide a custom split view, set this property at any time before you call super in the inherited viewDidLoad() method; that is, before the split view controller’s isViewLoaded property is true.

My NSSplitViewController subclass is called MainVC.

I tried setting my custom split view in -viewDidLoad before calling [super viewDidLoad]:

- (void)viewDidLoad {
    self.splitView = [MySplitView new];
    [super viewDidLoad];
    // Rest of viewDidLoad...
}

but it didn't work. I got the following error:

2017-09-02 10:35:43.527312-0700 Zee[6497:632581] ** * Assertion failure in -[MainVC setSplitView:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/AppKit/AppKit- 1561/Controllers/NSSplitViewController.m:220

2017-09-02 10:35:43.527558-0700 Zee[6497:632581] MainVC: The -splitView can only be assigned before the view is loaded

I also tried overriding loadView:

- (void)loadView {
    self.splitView = [MySplitView new];
    [super loadView];
}

But I get:

2017-09-02 10:39:39.377345-0700 Zee[6575:639146] ** * -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array

If I do the assignment after calling [super loadView], I get the same error as I did when I tried it in -viewDidLoad.

How do I use a custom NSSplitView in my NSSplitViewController subclass?

like image 206
sam Avatar asked Jan 04 '23 12:01

sam


2 Answers

The accepted answer in this thread is technically correct, but (understandably) misses a bug in NSSplitViewController that I haven't found documented anywhere. Such is the state of Cocoa development in 2017, I guess...

Anyway, the issue is this: if you're like me and wanted to use an NSSplitViewController with an NSSplitView that starts with just one view, the subclassing approach above won't work and you'll get the indexing error from OP. There's a private method (for drawing the divider) that assumes two NSSplitViewItem's are in there at all times, even though NSSplitView works fine with just one.

What I ended up doing is subclassing both NSSplitViewController and NSSplitView, and providing a check for if I create one that only has one view and swapping in a blank NSViewController instance that gets removed once the loading phase has settled down. I've also found that setting splitViewController.splitViewItems = ... directly doesn't work well, and you should be calling through to addSplitViewItem: to do this - presumably there's some stuff behind the scenes that you miss out on otherwise.

This class is annoyingly not documented, even though it's pretty useful overall. If you're a wayward traveler and you find this, hope it helps.

like image 55
Ryan McGrath Avatar answered Jan 11 '23 16:01

Ryan McGrath


So the index out of bounds issue is related to your split view not having any content rather than something you're doing wrong with initialization. Leaving the initialization in the loadView should be fine. Simply make sure you have initialized your NSSplitViewController subclass with at least 2 NSSplitViewItems before presenting it. Here's an example:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    MySplitViewController *vc = [MySplitViewController new];
    vc.splitViewItems = @[
        [NSSplitViewItem splitViewItemWithViewController:[MyViewController new]],
        [NSSplitViewItem splitViewItemWithViewController:[MyViewController new]]
    ];
    self.window.contentViewController = vc;
}
like image 32
Lucas Derraugh Avatar answered Jan 11 '23 14:01

Lucas Derraugh