I've got an OSX Cocoa app that has been built programatically (i.e., not with a NIB/XIB), which I'm trying to lay out using auto layout - but I'm getting some odd behaviour when the window first displays.
My main content is an NSView that holds has a collection of 100 NSButtons as subviews, laid out vertically. The buttons are all constrained relative to the NSView and each other; both the NSView and all the NSButtons have translatesAutoresizingMaskIntoConstraints=NO
set. I believe the code for the content view is good (i.e., no ambiguous layouts, etc), because if I set the main window's contentView to the NSView, the buttons display as expected.
However, if I set the main window's contentView to be an NSScrollView, and set the documentView of the NSScrollView to be the NSView, I get display problems.
On first display, I get a blank window - no scroll bars, nothing:
The NSScrollView has translatesAutoresizingMaskIntoConstraints=NO
. For debug purposes, I've also set the background colour of the NSScrollView to blue so that I can confirm what is being laid out where - but there's no blue shown anywhere.
But, as soon as I resize the window, the layout kicks in, and I get an NSScrollView the full size of the main window, with blue background, and scrollbars as expected:
I've read some references that suggest the problem is the lack of constraints on the clipView that is part of the NSScrollView. On that basis, I've tried setting up constraints binding [NSScrollView contentView]
to [NSScrollView documentView]
in the vertical and horizontal directions (with constant 0, multiplier 1, on the left, right, top and bottom). When I do this, the NSScrollView is now visible on first display, but it's the wrong size. The scroll doesn't scroll the full height of the internal content - the scrollable content scrolls as if it is the same size as the visible window. Lastly, the content overlaps the titlebar of the window:
Again, as soon as I resize the window, the constraints kick in, and the window displays as I'd expect (see the previous screenshot). So, I take it the extra constraints don't hurt, but they don't seem to be adding anything, either.
Further confusing matters - if I leave the buttons off altogether, and just use an empty NSView with no subviews as the content view, I get a full window of blue on startup, as I'd expect.
So - what's going on here? It feels like I'm missing a call to force the evaluation of constraints on the buttons; is that the case, or is something else going on here?
For those interested - here's my sample code. It's not Objective C - it's Python - but the language binding can convert Python method names into Objective C messages; the mapping to native ObjectiveC API should be obvious:
app = NSApplication.sharedApplication()
app.setActivationPolicy_(NSApplicationActivationPolicyRegular)
main_window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(
NSMakeRect(100, 100, 640, 480),
NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask,
NSBackingStoreBuffered,
False)
scrollview = NSScrollView.alloc().init()
scrollview.setHasVerticalScroller_(True)
scrollview.setHasHorizontalScroller_(True)
scrollview.setAutohidesScrollers_(True)
scrollview.setBorderType_(NSNoBorder)
scrollview.setTranslatesAutoresizingMaskIntoConstraints_(False)
scrollview.backgroundColor = NSColor.blueColor()
container = NSView.alloc().init()
container.setTranslatesAutoresizingMaskIntoConstraints_(False)
buttons = [
NSButton.alloc().init()
for b in range(0, 100)
]
for i, button in enumerate(buttons):
button.setBezelStyle_(NSRoundedBezelStyle)
button.setButtonType_(NSMomentaryPushInButton)
button.setTitle_(get_NSString('Button %s' % i))
button.setTranslatesAutoresizingMaskIntoConstraints_(False)
container.addSubview_(button)
if i == 0:
container.addConstraint_(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_(
button, NSLayoutAttributeTop,
NSLayoutRelationEqual,
container, NSLayoutAttributeTop,
1, 50,
))
else:
container.addConstraint_(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_(
button, NSLayoutAttributeBottom,
NSLayoutRelationEqual,
buttons[i-1], NSLayoutAttributeBottom,
1, 50,
))
container.addConstraint_(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_(
button, NSLayoutAttributeLeft,
NSLayoutRelationEqual,
container, NSLayoutAttributeLeft,
1, 50,
))
container.addConstraint_(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_(
button, NSLayoutAttributeRight,
NSLayoutRelationEqual,
container, NSLayoutAttributeRight,
1, -50,
))
container.addConstraint_(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_(
buttons[-1], NSLayoutAttributeBottom,
NSLayoutRelationEqual,
container, NSLayoutAttributeBottom,
1, -50,
))
scrollview.setDocumentView_(container)
main_window.setContentView_(scrollview)
main_window.makeKeyAndOrderFront_(None)
app.activateIgnoringOtherApps_(True)
app.run()