Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding and setting NSLayoutConstraint via code

I have some trouble with NSLayoutConstraint.

NSLayoutConstraint gives me an exception if I try to alter constant using the setConstant: method. I only have this problem when I add the height constraint via code.

First off, I'm getting the height constraint like so:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"firstAttribute = %d", NSLayoutAttributeWidth];
NSArray *filteredArray = [[self constraints] filteredArrayUsingPredicate:predicate];
return filteredArray[0];

Which gives me the right constraint. I have a NSTextField subclass where this works perfectly. The constraints are set in the Interface Builder, and I can set and alter the constant.

Now I have a view, where I add different subviews at run-time. Those subviews are located in own NIBs, which means I can't pin their width and height. So I thought I'd add the constraints as soon as the view is being added to a superview. This is being executed in viewDidMoveToSuperview.

// Pin Width & Height
[self addConstraint:[NSLayoutConstraint constraintWithItem:self
                             attribute:NSLayoutAttributeWidth
                             relatedBy:NSLayoutRelationEqual
                                toItem:nil
                             attribute:NSLayoutAttributeNotAnAttribute
                            multiplier:1.0f
                              constant:self.frame.size.width]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self
                                                 attribute:NSLayoutAttributeHeight
                                                 relatedBy:NSLayoutRelationEqual
                                                    toItem:nil
                                                 attribute:NSLayoutAttributeNotAnAttribute
                                                multiplier:1.0f
                                                  constant:self.frame.size.height]];

The constraints are added, I can confirm this with an NSLog.

"<NSLayoutConstraint:0x100605cc0 H:[ITControlPanelChildView:0x100616eb0(269)]>",
"<NSLayoutConstraint:0x100614410 V:[ITControlPanelChildView:0x100616eb0(317)]>"

Now, finally, when I try to alter the constraint using [constraint setConstant:somethingDifferent]; I get the following exception:

Unable to simultaneously satisfy constraints:
(
    "<NSLayoutConstraint:0x10192fcb0 V:[ITControlPanelChildView:0x10192e4f0(100)]>",
    "<NSAutoresizingMaskLayoutConstraint:0x10053ff10 h=--& v=&-- V:[ITControlPanelChildView:0x10192e4f0(317)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x10192fcb0 V:[ITControlPanelChildView:0x10192e4f0(100)]>

This is exactly the constraint I'm trying to change. Can anyone explain to me why this is happening?


EDIT

I just read that the NSAutoresizingMaskLayoutConstraints are added automatically, which you can disable if you set [self setTranslatesAutoresizingMaskIntoConstraints:NO];.

If I disable it, it works.

Even better would be if I could access the NSAutoresizingMaskLayoutConstraint which are created. Does anyone know how this works?

like image 226
IluTov Avatar asked Dec 19 '12 17:12

IluTov


2 Answers

As indicated in the rest of the log message that you haven't included, you've got autoresizing constraints that you don't want or need.

You can remove these when you add the subview. In the method you're using above, just add this line:

self.translatesAutoresizingMaskIntoConstraints = NO;

Though if you are creating the view in a separate nib, where is it getting its size from? Doesn't it already have size constraints which you can just create outlets to?

like image 116
jrturton Avatar answered Nov 09 '22 16:11

jrturton


I have had this problem twice.

I used a technique described in Cocoa Programming for Mac OSX 4th Ed by Hillegass & Preble. The book advises using an NSBox as a view container and switching the views into it using code. The book also includes some handy code for manually resizing the external containing window to hold the view you just switched to.

The first time I tried their technique I got this exception:

2013-01-05 22:56:05.103 MyApp[17717:303] Unable to simultaneously satisfy constraints:
(
    "<NSAutoresizingMaskLayoutConstraint:0x100141330 h=--& v=--& H:[NSView:0x10012fdf0(4)]>",
"<NSLayoutConstraint:0x1001316c0 H:[NSScrollView:0x100123ff0]-(NSSpace(20))-|   (Names: '|':NSView:0x10012fdf0 )>",
"<NSLayoutConstraint:0x1001315d0 H:|-(NSSpace(20))-[NSScrollView:0x100123ff0]   (Names: '|':NSView:0x10012fdf0 )>")

Will attempt to recover by breaking constraint 

Frustratingly enough I had to refactor the UI and in the rebuilt version (which is based on NSDocument rather than a simple window) I struck the same problem again.

If I set translatesAutoResizingConstraints to NO on the sub-views they don't get laid out properly. But those sub-views are complex so its not a solution though because my windows are not laid out. That does stop the exception from being thrown however.

If I turn off AutoLayout on the top level NSDocument based window XIB - then the exception also stops being thrown. But then the layout for the top level elements is wrong, and the box is not sized correctly. This is not so complex that I couldn't manually lay it all out I guess.

The weird thing is on my first version of the UI it works with no throw. The first version has Autolayout set for the top-level view and all the subviews.

I cannot for the life of me find what obscure setting is turned on in one, but not the other. I pasted the exact same code that works in the window version for swapping views into the document based version, and I went thru control by control checking every setting.

My memory of fixing the issue the first time is that I had turned off AutoLayout on the XIB, but looking at it now trying to fix for the second time, the AutoLayout is turned on and it works fine.

First version works - second one doesn't. The only thing I can think of is that somehow something in NSDocument or something else is causing the issue.

However I did find a way to fix it in case number two: turn off the "AutoResizes Subviews" check-box for the NSBox used as a view container.

Why this is not required in the first version of the UI (which works fine with that checkbox set) is a mystery.

Anyway - documented here in case it helps someone else.

like image 29
Sez Avatar answered Nov 09 '22 17:11

Sez