ericg
ericg

Reputation: 8722

How to add constraints after an addSubview so the subview resizes with the superview

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?

Upvotes: 8

Views: 17071

Answers (1)

NKorotkov
NKorotkov

Reputation: 3681

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 :)

enter image description here enter image description here

Upvotes: 18

Related Questions