Resize current view using NSLayoutConstraints
Solution 1
As it turns out, NSPopoverController
is an exception to the rule, and translatesAutoresizingMaskIntoConstraints
must be YES
for autolayout to work correctly.
Solution 2
There are a couple of issues:
-
If you want your constraints to define the size of their containing view, they can't be ambiguous, as they are now. They have to fully define the space they need. For example:
NSDictionary *views = @{@"v1" : self.v1, @"v2" : self.v2}; // Set horizontal constraints [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[v1(200)]-|" options:0 metrics:nil views:views]]; [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[v2(==v1)]-|" options:0 metrics:nil views:views]]; // Set vertical constraints [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[v1]-[v2(600)]-|" options:0 metrics:nil views:views]]; // Initialise height constraint for v1 self.v1Height = [NSLayoutConstraint constraintWithItem:self.v1 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:50]; [self addConstraint:self.v1Height];
Note, I'm setting the width of
v1
, I'm settingv2
to be the same width asv1
, and I'm setting the height ofv2
, also. That, combined with your subsequent creation of thev1Height
constraint, now makes the view's layout unambiguous. -
Having done that, you also need to tell your view controller to inform the popover controller of its content size when the autolayout has been applied. Thus, your view controller could implement a
viewDidLayoutSubviews
:- (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; self.view.frame = CGRectMake(0.0, 0.0, self.view.frame.size.width, self.view.frame.size.height); self.contentSizeForViewInPopover = self.view.frame.size; }
Note, that setting of the
frame
is very curious, but if you don't reset the origin to{0.0, 0.0}
, your controls won't appear in the right place. It doesn't seem right that you have to do that, but in my experience, you do. -
Somewhat unrelated to your problem, you mention that you're removing
v1Height
"by removing it, recreating it, and then readding it". That's unnecessary and not recommended. Theconstant
property is modifiable. As the docs say"Unlike the other properties, the
constant
may be modified after constraint creation. Setting theconstant
on an existing constraint performs much better than removing the constraint and adding a new one that's just like the old but for having a newconstant
."Thus, that means that you can do something as simple as:
self.v1Height.constant = newHeight; [UIView animateWithDuration:0.5 animations:^{ [self layoutIfNeeded]; }];
Solution 3
Better later than never...
For Storyboard, IB and Swift:
Just control-drag your constraint like any other control to your viewController
@IBOutlet weak var spaceToTopThatIDecided: NSLayoutConstraint!
then change it simply:
spaceToTopThatIDecided.constant = 44
That's all!
sapi
Updated on June 05, 2022Comments
-
sapi almost 2 years
I'm trying to set the layout of a view using
NSLayoutConstraint
s.I can set things up within the view correctly, but I also need to be able to resize the current view (the one that I'm setting the constraints for) if the constraints require it. In other words, I want to set a fixed content size with a flexible window, rather than the reverse.
So basically, I have:
NSDictionary *views = [NSDictionary dictionaryWithObjectsAndKeys:self.v1, @"v1", self.v2, @"v2", nil]; // Set horizontal constraints [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[v1]-|" options:0 metrics:nil views:views]]; [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[v2]-|" options:0 metrics:nil views:views]]; // Set vertical constraints [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[v1]-[v2]-|" options:0 metrics:nil views:views]]; // Initialise height constraint for v1 self.v1Height = [NSLayoutConstraint constraintWithItem:self.v1 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:50];
If I later change
self.v1Height
(by removing it, recreating it, and then readding it), I would like the frame ofself
to expand (or contract) to accommodate the changed height.I'm not sure if it's relevant (as I'm more familiar with iOS than OS X), but this is the content view of a
NSPopover
.What constraint do I need to add to achieve this?