简体   繁体   中英

Using auto-layout in a custom view where constraints rely on frame

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM