Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How forgiving is Auto Layout when adding/removing constraints?

Tags:

ios

autolayout

How lenient is the Auto Layout system when adding and removing constraints? Will it cause crashes, warnings, or will nothing happen when I use it incorrectly?

In particular, what happens when I do the following?

  1. Remove a constraint that isn't owned by the target?
  2. Add a constraint that is already owned by the target?
  3. Add a constraint to a view that is above the nearest common ancestor?
  4. Add a constraint to a view that is below the nearest common ancestor?
  5. Add a constraint that is already owned by a view to a new (valid) view?
like image 380
Senseful Avatar asked Dec 07 '25 09:12

Senseful


1 Answers

Imagine we have the following UIView hierarchy:

  • grandFather
    • father
      • target
        • son
          • grandson
    • uncle

We have 4 constraints defined (grandFatherConstraint, fatherConstraint, targetConstraint, and sonConstraint) which simply attaches the named view with the one right below it. The owner of the constraint is the nearest common ancestor, which happens to be the one mentioned in the constraint's name (e.g. grandFatherConstraint is owned by grandFather).

For example, grandFatherConstraint pins grandFather to father, making grandFather the nearest common ancestor of this constraint. Similarly, targetConstraint pins target to son, and target is the nearest common ancestor.

1. Remove a constraint that isn't owned by the target?

  • Owned by descendent? (e.g. [target removeConstraint:sonConstraint])
  • Owned by ancestor? (e.g. [target removeConstraint:grandFatherConstraint])
  • Owned by different view? (e.g. [target removeConstraint:uncleConstraint])
  • Owned by nothing? (e.g. [target removeConstraint:nothingConstraint])
  • View which is only part of the constraint, but still not the owner? (e.g. [target removeConstraint:fatherConstraint])

In all of these cases, nothing happens. The constraint is not removed.

This is expected behavior according to the removeConstraint: documentation:

Removing a constraint not held by the view has no effect.

2. Add a constraint that is already owned by the target?

Example: [target addConstraint:targetConstraint]

Nothing happens, it does not add the constraint a second time.

3. Add a constraint to a view that is above the nearest common ancestor?

Example: [target addConstraint:newSonConstraint]

Everything will appear correct, however the owner of the constraint will not be the nearest common ancestor, which may (or may not) be the behavior you want. In other words, it will attach the constraint to the view you specified (target) rather than finding and attaching it to the nearest common ancestor (son).

The reason this may be desirable is because the owner of the constraint is significant in certain cases. From the addConstraint: documentation:

Constraints that are added to a view are said to be held by that view. The coordinate system used when evaluating the constraint is the coordinate system of the view that holds the constraint.

4. Add a constraint to a view that is below the nearest common ancestor?

Example: [target addConstraint:newFatherConstraint]

This will cause a crash.

Upon installing the constraint, the following will be logged:

The view hierarchy is not prepared for the constraint: [...]
When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug.

Upon releasing control to the main run loop, you will get a crash with the following message:

View hierarchy unprepared for constraint.
Constraint: [...]
Container hierarchy: [...]
View not found in container hierarchy: [...]
That view's superview: [...]

This is expected behavior according to the addConstraint: documentation:

The constraint may only reference the view itself or its subviews.

5. Add a constraint that is already owned by a view to a new (valid) view?

Example: [father addConstraint:targetConstraint]

This is an interesting scenario which isn't fully documented. When you run the above code, it will first remove the constraint from its current owner (target) and then reassign it to the new view you specified (father).

Therefore, the above code essentially becomes:

[target removeConstraint:targetConstraint]
[father addConstraint:targetConstraint]
like image 172
Senseful Avatar answered Dec 10 '25 00:12

Senseful