问题
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 of self
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?
回答1:
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]; }];
回答2:
As it turns out, NSPopoverController
is an exception to the rule, and translatesAutoresizingMaskIntoConstraints
must be YES
for autolayout to work correctly.
回答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!
回答4:
I found a easy solution to update constraints (resize ) with a category:
Hide autolayout UIView : How to get existing NSLayoutConstraint to update this one
来源:https://stackoverflow.com/questions/17130136/resize-current-view-using-nslayoutconstraints