Banana Split: Overcoming Problems with NSSplitView

There are a bunch of bugs or misbehaviors in the current (10.4) version of NSSplitView. Here are the ones I’ve needed to solve:

(1) While the split view respects the minimum and maximum coordinates if you drag the divider around yourself, it doesn’t respect them if you’ve set up the view to resize with the window, and you shrink the window.

(2) If you collapse a subview, any view in it that has keystroke focus retains it. You can still type in invisible text fields, navigate invisible tables, etc. Plus, all the text fields, tables, etc. inside the collapsed subview can still be tabbed through.

(3) The coordinates of the subviews are not automatically saved and restored, the way the column sizes and positions in a NSTableView are.

In my recent project, I fixed all these problems with delegate methods. If you implement splitView:resizeSubviewsWithOldSize: and do the resizing yourself in the problem case, you can fix (1). If you implement splitViewDidResizeSubviews:, you can do (3) and, if you check for when a subview has collapsed, you can make sure (2) doesn’t happen.

However, if you want to write less of your own code, there are other options. Even with a cursory search, I have found three NSSplitView replacements:

OASplitView from the Omni frameworks. Is a subclass of NSSplitView. Solves only (3).

KFSplitView from Ken Ferry. A subclass of NSSplitView. It solves (1) and (3). (2) still happens in its demo.

RBSplitView from Rainer Brockerhoff. Not a subclass of NSSplitView. Solves (1) and (3). Solves the first part of (2), but not the second.

Admittedly, the second part of (2) might not be possible in general-purpose framework code, since it can involve an invasive solution. I solved it in the code I was working on by subclassing the NSTableView located in the subview. In the subclass, I added an _acceptsFirstResponder flag, a setAcceptsFirstResponder: method to set the flag, and overrode acceptsFirstResponder to use the flag.

With these changes, it was easy to take a table out of the tab chain when the subview was collapsed, by merely sending it the set message. It would have been more onerous if I had to subclass multiple classes to get this same behavior in different types of widgets, in order to send the same set message in them all.

You may be able to get the same behavior out of some controls by sending them a setEnabled: message, but that wasn’t successful for me with tables.

2 comments

  1. Brad Miller

    I highly recommend RBSplitView. I’ve used it in few projects now and it is a lot nicer to work with the NSSplitView. I’m going to let Rainer know about (2) also. Not sure if he can do anything about it, but he usually likes the challenge.

    One correction, KFSplitView is a subclass of NSSplitView. It also has the drawback of it not being licensed for commercial apps. The others you can.

  2. Andrew Pontious

    Heh, knew I was gonna screw up *something*. I will make the correction.

    And now that I think about it, the second part of (2) is probably better solved at a lower level in the AppKit framework.

    As I said in the post, I was unable to use existing APIs to turn off firstResponder status for tables. What might be needed is a new, lower-level API that can be invoked for all widgets that ever return YES for acceptsFirstResponder.