Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 597
Scott Berrevoets Avatar asked Jan 21 '14 17:01

Scott Berrevoets


People also ask

What are auto layout constraints?

Auto Layout constraints allow us to create views that dynamically adjust to different size classes and positions. The constraints will make sure that your views adjust to any size changes without having to manually update frames or positions.

When should I use Auto Layout Figma?

Auto layout is a property you can add to frames and components. It lets you create designs that grow to fill or shrink to fit, and reflow as their contents change. This is great when you need to add new layers, accommodate longer text strings, or maintain alignment as your designs evolve.

What is the shortcut to creating an auto layout frame on Figma?

Auto-Layout Shortcuts SHIFT + A = add auto layout. SHIFT + ALT + A = turn auto layout frame into a regular frame.

What are the different types of auto layout?

There are three main types of Auto Layout issues: ambiguous layouts, unsatisfiable constraints and logic errors. You can solve Auto Layout issues faster using UIView methods, Debug View Hierarchy and Interface Builder.


Video Answer


1 Answers

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.

like image 120
jrturton Avatar answered Sep 27 '22 20:09

jrturton