问题
I'm writing a custom view that is initialized programmatically. I override updateConstraints
to add all the constraints required for this view. :
- (void)updateConstraints {
[self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
[self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeTop multiplier:1 constant:0]];
[self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
// some more constraints, you get the point
self.bottomSpacingConstraint = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1 constant:-(0.2 * CGRectGetHeight(self.bounds))];
[self addConstraint:self.bottomSpacingConstraint];
[super updateConstraints];
}
The problem is that self.bounds
returns the equivalent of CGRectZero
. I did my research and according to this objc.io article, that's expected as the frame doesn't get set until layoutSubviews
is called. It also mentions
To force the system to update the layout of a view tree immediately, you can call
layoutIfNeeded
/layoutSubtreeIfNeeded
(on iOS and OS X respectively). This can be helpful if your next steps rely on the views’ frame being up to date.
However, when I add
[self setNeedsLayout];
[self layoutIfNeeded];
right before setting self.bottomSpacingConstraint
in updateConstraints
, I still get a CGRectZero
back for the frame. According to the objc.io article (and this SO answer), these methods should trigger layout and update the frame.
Can anybody shine some light on how to make this all work? I'm interested in the solution as well as an explanation of what causes which layout-related methods to be called (for example, it appears that changing an existing constraint's constant in layoutSubviews
causes setNeedsUpdateConstraints
to be called, which then triggers updateConstraints
and causes constraints to be added multiple times).
回答1:
I'm pretty sure that you can't, or shouldn't, call layoutIfNeeded
from updateConstraints
. Updating the constraints is in an earlier part of the layout cycle, so I don't think it will have the effect you are after.
In your case the solution would be to check the the constant
property of your frame-dependent constraint in layoutSubviews, and if it needs updating, either update it there or call setNeedsUpdateConstraints
(be careful about causing loops).
You've said updating the constraint triggers another call to updateConstraints
- this is true, and I think you are misusing updateConstraints
- it is for updating constraints based on changes to your view's content. You should only be adding those constraints if they don't already exist.
来源:https://stackoverflow.com/questions/21265373/using-auto-layout-in-a-custom-view-where-constraints-rely-on-frame