NSSplitViewController Auto Layout Bug and Workaround
31st March, 2015 —
You can’t constrain this!
NSSplitViewController has an edge-case Auto Layout bug that manifests when a split view item is set as collapsed in Interface Builder (IB) and then shown at some point after
viewDidLoad (e.g., in response to a user action).
It actually took me two days to track down this little rascal so I took the time to create a simple isolated example that replicates the issue.
Download the source here. (Test.zip, 68KB)
The exception thrown by the sample is below:
Error console output:
2015-03-31 13:46:32.669 Test[7890:1234827] Unable to simultaneously satisfy constraints: ( "<NSLayoutConstraint:0x6080000850a0 H:[_NSSplitViewItemViewWrapper:0x6000001411e0(50)]>", "<NSLayoutConstraint:0x6000000879e0 H:|-(0)-[NSView:0x6000001221c0] (Names: '|':_NSSplitViewItemViewWrapper:0x6000001411e0 )>", "<NSLayoutConstraint:0x600000087a30 H:[NSView:0x6000001221c0]-(0)-| (Names: '|':_NSSplitViewItemViewWrapper:0x6000001411e0 )>", "<NSLayoutConstraint:0x600000087620 H:[NSBox:0x10052d0a0'Box'(560)]>", "<NSLayoutConstraint:0x600000087760 H:|-(20)-[NSBox:0x10052d0a0'Box'] (Names: '|':NSView:0x6000001221c0 )>", "<NSLayoutConstraint:0x6000000877b0 H:[NSBox:0x10052d0a0'Box']-(20)-| (Names: '|':NSView:0x6000001221c0 )>" ) Will attempt to recover by breaking constraint <NSLayoutConstraint:0x600000087620 H:[NSBox:0x10052d0a0'Box'(560)]> Set the NSUserDefault NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints to YES to have -[NSWindow visualizeConstraints:] automatically called when this happens. And/or, break on objc_exception_throw to catch this in the debugger.
As far as I can tell, the problem appears to be this: When Cocoa sees a collapsed split view item, it automatically creates a width constraint with required priority on the also-automatically-created
_NSSplitViewItemViewWrapper. Then, if your split view item’s view also has a required width constraint of some sort, they conflict when you set
collapsed = false later on.
The isolated example I’ve provided should hopefully make it easy for Apple to fix this but, in the meanwhile, there is an easy workaround: Either,
- Leave the Collapsed checkbox unchecked in IB and set the split view item’s
collapsedproperty to true in the
viewDidLoadmethod of your
- Check the Collapsed checkbox in IB and then, in your
Swift:Simply setting the
mySplitViewItem.collapsed = false mySplitViewItem.collapsed = true
falsemomentarily and then setting it to
trueis enough, even though it happens on the same stack frame. I can only assume that doing so results in the width constraint of the view being measured and then correctly and applied to the
_NSSplitViewItemViewWrapper’s width constraint.
I hope this helps you work around the problem and makes it easier for the folks at Apple to fix it. (rdar://20432741)