Whoa
Whoa

Reputation: 1334

Autolayout & programmatic constraints: How to deal with updateConstraints firing multiple times?

When programmatically creating layouts, I follow Apple's advice: override -updateConstraints, add custom constraints, and call -setNeedsUpdateConstraints once subviews have been added to the view. My typical setup looks like this:

- (void)setupViews
{
    //Style View

    //Add gesture recognizers

    //Add Subviews


    [self setNeedsUpdateConstraints];
}

- (void)updateConstraints
{
    //Add custom constraints       

    [super updateConstraints];
}

The problem

There are occasions when -updateConstraints gets fired multiple times (for example, when a view's controller is presented or pushed w/ animation). The problem here is that each constraint added gets re-added. This becomes a serious problem when trying to on-demand change the constant of an added constraint, since there are two of the original constraint that subsequently conflict with each other. I imagine that even when you aren't manipulating the constraints after creating them, having double what you doesn't seem good.

Potential solutions

1 - Remove all constraints effecting the view before applying them in -updateConstraints:

 - (void)updateConstraints
    {   
        //Remove all constraints affecting view & subviews

        //Add custom constraints

        [super updateConstraints];       
    }

2 - Set a layout flag & check against it before adding custom constraints:

- (void)updateConstraints
{
    if (self.didAddConstraints) {
        [super updateConstraints];
        return;
    }

    //Add custom constraints   

    self.didAddConstraints = YES;    

    [super updateConstraints];
}

3 - Don't worry about doubling up on constraints, and whenever changing a constant is needed, only remove that constraint before re-adding.

3 - Something awesome that I haven't thought of.


What's the best practice here?

Upvotes: 19

Views: 8548

Answers (2)

duci9y
duci9y

Reputation: 4168

Short answer: Potential solution number 2.

Removing and reapplying all constraints may become costly as the layout becomes more complex. Besides, if your layout is stateful, you'd have more problems.

Doubling constraints is very inefficient, you can never know how many times updateConstraints might be called.

As this blog post shows, using a flag is the simplest, most efficient way of dealing with this problem. That is how I deal with it myself.

As a side note, you mention of there existing an awesome way that you have not thought of yet. Most of the times, the simplest way IS the most awesome way. :)

Upvotes: 17

user1951992
user1951992

Reputation:

This kind of tracking could be done too, for the initial setup. In most cases.

override func updateConstraints() {
            if constraints.count == 0 {
                let views = ["textField": textField]
                addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[textField]-0-|", options: [], metrics: nil, views: views))
                addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[textField]-0-|", options: [], metrics: nil, views: views))
            }
            super.updateConstraints()
        }

Upvotes: 0

Related Questions