I have a sample project which demonstrates the problem here
https://github.com/ericgorr/autolayout_with_addsubview.git
I have a view called CalcView which I want to programmatically add as a subview to a view on the main window for the app. When I resize my window, I want CalcView to be resized.
In windowDidLoad in MainWindowController, I add the subview by doing:
let calcViewController = ELIZCalcView()
let calcView = calcViewController.view
calcContentView?.addSubview( calcViewController.view )
I try to add the constraints by doing:
let bindings = [ "calcView": calcView ]
let horizontalContraint:[AnyObject] = NSLayoutConstraint.constraintsWithVisualFormat( "H:|[calcView]|", options: NSLayoutFormatOptions(0), metrics: nil, views: bindings )
let verticalContraint:[AnyObject] = NSLayoutConstraint.constraintsWithVisualFormat( "V:|[calcView]|", options: NSLayoutFormatOptions(0), metrics: nil, views: bindings )
calcContentView?.addConstraints( horizontalContraint )
calcContentView?.addConstraints( verticalContraint )
Now, for someone who knows how to properly interpret that code, it is likely very apparent that it will not work. After I run my app, I cannot resize the window at all. Additionally, I see the following error message in the console:
2015-07-04 16:04:45.019 aocsCalc[5797:3526462] Unable to simultaneously satisfy constraints: (
"<NSLayoutConstraint:0x618000082440 V:|-(0)-[NSView:0x600000120f00] (Names: '|':aocsCalc.ELIZHighlightView:0x608000120500 )>",
"<NSAutoresizingMaskLayoutConstraint:0x618000084100 h=--& v=&-- V:|-(-2)-[NSView:0x600000120f00] (Names: '|':aocsCalc.ELIZHighlightView:0x608000120500 )>" )
Will attempt to recover by breaking constraint <NSLayoutConstraint:0x618000082440 V:|-(0)-[NSView:0x600000120f00] (Names: '|':aocsCalc.ELIZHighlightView:0x608000120500 )>
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.
If I remove the vertical constraint, the error message goes away and I can resize the window vertically.
So, what simple thing do I need to do so CalcView is resized along with the window?
If you remove SUBVIEW by simply checking all the CONTAINERVIEW constraints you could see that two aren't around anymore.
Auto Layout constraints allow us to create views that dynamically adjust to different size classes and positions. The constraints will make sure that your views adjust to any size changes without having to manually update frames or positions.
Auto Layout defines your user interface using a series of constraints. Constraints typically represent a relationship between two views. Auto Layout then calculates the size and location of each view based on these constraints. This produces layouts that dynamically respond to both internal and external changes.
Before you add your calcView as subview try inserting this line of code:
calcView.translatesAutoresizingMaskIntoConstraints = false
This should solve your problem.
By default on UIView/NSView this property is set to YES/true and it creates it's own set of constraints based on autoresizing mask. These auto-made constraints conflict with the ones you've created in code.
It clearly says so in the error description too. On lines 2 and 3 it shows you that there's 2 set of vertical constraints regarding one view - NSView:0x600000120f00
, which appears to be your calcView.
<NSLayoutConstraint:0x618000082440 V:|-(0)-[NSView:0x600000120f00]
<NSAutoresizingMaskLayoutConstraint:0x618000084100 h=--& v=&-- V:|-(-2)-[NSView:0x600000120f00]
They are both vertical. First one wants to snap view to the superview's top with no margin. The second one (created automatically) wants to snap it with a small margin, presumably taken from how it is layed out in Interface Builder.
UPDATE
Create a new Cocoa Application project and paste the following code:
override func viewDidLoad() {
super.viewDidLoad()
let view = NSView()
view.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(view)
//Making it red just to see a little better. Ignore this two lines.
view.wantsLayer = true
view.layer?.backgroundColor = CGColorCreateGenericRGB(1, 0, 0, 1)
//-----------------------------------------------------------------
let views = ["view" : view]
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-[view]-|", options: NSLayoutFormatOptions(0), metrics: nil, views: views))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[view]-|", options: nil, metrics: nil, views: views))
}
Newly created NSView is snapped to the View Controllers main view with standard margins (8 points, it's described as "-" in the visual format string) and is resizing with the main view (see pictures). A little tip - you don't have to specify the "H:" in the visual format, only "V:". It's horizontal by default.
This should give you a good idea of how adding constraints programmatically works. Code might not be optimal, I code in Obj-C and know very little Swift.
I've downloaded your project. Error probably lies somewhere in your complicated view hierarchy and xib manipulation. But that's a whole other story. Also be careful with scroll views, they are a bit tricky when it comes to autolayout, you can find a lot of coverage on that on SO. Happy coding :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With