Created
November 27, 2018 09:45
-
-
Save helje5/0456aafe2a27b4ed37ce08bb7a53f133 to your computer and use it in GitHub Desktop.
Nested NSStackViews lead to ambiguous layout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Cocoa | |
final class TestView : NSView { | |
static func makeAndAttachToView(_ rootView: NSView) { | |
let view = TestView() | |
view.translatesAutoresizingMaskIntoConstraints = false | |
rootView.addSubview(view) | |
NSLayoutConstraint.activate([ | |
view.leadingAnchor .constraint(equalTo: rootView.leadingAnchor), | |
view.trailingAnchor.constraint(equalTo: rootView.trailingAnchor), | |
view.topAnchor .constraint(equalTo: rootView.topAnchor), | |
view.bottomAnchor .constraint(equalTo: rootView.bottomAnchor) | |
]) | |
} | |
override init(frame: NSRect) { | |
super.init(frame: frame) | |
NSLayoutConstraint.activate([ | |
widthAnchor .constraint(equalToConstant: 320), | |
heightAnchor.constraint(equalToConstant: 200) | |
]) | |
func makeStackView(_ orientation : NSUserInterfaceLayoutOrientation, | |
_ alignment : NSLayoutConstraint.Attribute, | |
_ view : NSView) | |
-> NSStackView | |
{ | |
// The subclasses are just for debugging in the Xcode View Debugger | |
let v = NSStackView(views: [view]) | |
v.translatesAutoresizingMaskIntoConstraints = false | |
v.orientation = orientation | |
v.alignment = alignment | |
v.spacing = 2.0 | |
return v | |
} | |
let searchField = NSSearchField(frame: NSMakeRect(0, 0, 128, 128)) | |
#if false // not ambiguous | |
let view = makeStackView(.horizontal, .centerY, searchField) | |
#else // ambiguous | |
let view = makeStackView(.horizontal, .centerY, | |
makeStackView(.vertical, .width, // .leading | |
searchField) | |
) | |
#endif | |
addSubview(view) | |
NSLayoutConstraint.activate([ | |
view.leadingAnchor .constraint(equalTo: self.leadingAnchor), | |
view.trailingAnchor.constraint(equalTo: self.trailingAnchor), | |
view.topAnchor .constraint(equalTo: self.topAnchor), | |
view.bottomAnchor .constraint(equalTo: self.bottomAnchor) | |
]) | |
} | |
required init?(coder decoder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
} |
This removes the ambiguity:
let innerStack = makeStackView(.vertical, .width, // .leading
searchField)
innerStack.setHuggingPriority(.defaultHigh, for: .vertical) // <=
let view = makeStackView(.horizontal, .centerY, innerStack)
Note: it really is the NSStackView
specific setHuggingPriority(_:for:)
, not setContentHuggingPriority(_:for:)
, which still results in the ambiguity.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The setup seems simple: We have a search-field, which is nested in a v-stack which itself is nested in an h-stack. As soon as I nest one stack in another, the layout becomes ambiguous. And I'm not quite sure why.
Looks right (a more complex variant jumps a few pixels now and then, hence it seems to be a proper ambiguity):

But Xcode says the height/y-pos are ambiguous:

.centerY
).>=
the height of its contents?What is the proper fix? Explicitly constrain the hstack to its largest child? That seems to defeat the idea of a stack view?
P.S.: To test the code, just create a new AppKit Xcode project, wire up the window's root view to an outlet, then attach the
TestView
usingTestView.makeAndAttachToView(rootView)
, like so: