Resolving Auto Layout Issues

Auto Layout issues occur when you create conflicting constraints, when you don’t provide enough constraints, or when the final layout contains a set of constraints that are ambiguous. When an issue occurs, the Auto Layout system will try to degrade as gracefully as possible so that your app remains usable, but it’s important to understand how to catch layout problems in development. Auto Layout offers several features to help you find and fix the source of errors, including visual hints in Interface Builder that identify which issue is occurring so that you can resolve it with the appropriate solution.

Identifying Issues

Interface Builder displays Auto Layout issues in a number of different places:

In the Issues Navigator. The Navigator groups issues by type.

../Art/debug_navigator.png

In the Interface Builder outline view. If there are any issues in the top-level view that you’re editing on the canvas, the Interface Builder outline view shows a disclosure arrow. This arrow is red if there are conflicts or ambiguities, and yellow if constraints result in view misplacement.

../Art/red_arrow_in_outline_view_2x.png

Click the disclosure arrow, and you’ll see a list of the issues separated by type, with information about the relevant controls and constraints. Click the error or warning symbol next to any issue to see a description of what’s happening and a recommended course of action.

../Art/debug_info_view.png

On the canvas. Misplaced or ambiguous constraints are shown in orange, conflicting constraints are red, and red dotted frames show the predicted runtime positions of misplaced or ambiguous views; these issues are described in the following sections.

Resolving View Misplacement

In Interface Builder, constraints and frames are separate. If there is a mismatch for a view between the frame you’re seeing on the canvas and the runtime position based on the current set of constraints, you have view misplacement.

Interface Builder shows misplaced views by drawing constraints that are no longer satisfied in orange, with a misplacement delta. It also displays a dotted red frame to show you where the view will be at runtime.

../Art/misplacement.png
bullet
To resolve misplacement

Do one of the following:

  • Choose Issues > Update Frames. This causes the element to move back to where it was before it was misplaced. The frames change to satisfy existing constraints.

  • Choose Issues > Update Constraints. This causes the constraints to be updated to the new location of the element. The constraints change to match the current frames.

Resolving Constraint Conflicts

Conflict occurs when you have a set of constraints that Auto Layout can’t satisfy, such as two different widths for an element. Conflicting constraints show up on the canvas in red, drawn at actual size.

../Art/conflict.png
bullet
To resolve a constraint conflict
  • Delete one of the conflicting constraints.

Resolving Ambiguity

Ambiguity occurs when the constraints you’ve defined result in multiple possible solutions for views’ size and placement; this may be because, for example, there are not enough constraints, or because a view’s content size is undefined.

Interface Builder shows ambiguity by drawing frames with orange borders; to learn more about the ambiguity, use the Issues navigator. The way you resolve ambiguity depends on the condition that’s causing it.

../Art/ambiguity_no_vertical.png

One or More Constraints Are Missing

In the simplest case, there are just not enough constraints—for example, you constrained the horizontal position of an element, but didn’t add constraints for the vertical position. To resolve the ambiguity, you need to add the missing constraints

bullet
To add missing constraints
  • Choose Issues > Add Missing Constraints.

Content Size Is Undefined

Some container views—such as a stack view—depend on the size of their content to determine their own size at run time, which means the size is unknown at design time. To address this problem, you should set placeholder constraints, such as a placeholder minimum width for a view (this placeholder is removed at build time, when your view defines its size).

bullet
To create a placeholder constraint
  1. Add the desired constraint as normal.

  2. Select the constraint, then open the Attributes inspector.

  3. Select the checkbox next to the Placeholder option.

    ../Art/placeholder.png

The Size of Custom View Content Is Unknown

Unlike standard views, custom views have no defined intrinsic content size. During design time, Interface Builder doesn’t know what size to expect a custom view to be. To address this problem, you should set a placeholder intrinsic content size to indicate the custom view’s content size.

bullet
To set a view’s placeholder intrinsic content size
  1. With the view selected, open the Size inspector.

    ../Art/size_inspector.png
  2. Under the Intrinsic Size option, select Placeholder.

  3. Set appropriate values for the width and height.

    ../Art/intrinsic_content_size.png

Debugging in Code

Broadly speaking, there are two phases to debugging a layout problem:

  1. Map from “this view is in the wrong place” to “this constraint (or these constraints) is (are) incorrect.”

  2. Map from “this constraint is incorrect” to “this code is incorrect.”

bullet
To debug a problem with Auto Layout
  1. Identify the view with an incorrect frame.

    It may be obvious which view has the problem; if it is not, you may find it helpful to use the NSView method _subtreeDescription to create a textual description of the view hierarchy.

  2. If possible, reproduce the issue while running under the Auto Layout template in Instruments.

  3. Find the bad constraint or constraints.

    To get the constraints affecting a particular view, use constraintsAffectingLayoutForOrientation: in OS X or constraintsAffectingLayoutForAxis: in iOS.

    You can then inspect the constraints in the debugger, which prints constraints using the visual format notation described in “Visual Format Language.” If your views have identifiers, they are printed out in the description, like this:

    <NSLayoutConstraint: 0x400bef8e0 H:[refreshSegmentedControl]-(8)-[selectViewSegmentedControl] (Names: refreshSegmentedControl:0x40048a600, selectViewSegmentedControl:0x400487cc0 ) >

    otherwise the output looks like this:

    <NSLayoutConstraint: 0x400cbf1c0 H:[NSSegmentedControl:0x40048a600]-(8)-[NSSegmentedControl:0x400487cc0]>
  4. If it’s not obvious which constraint is wrong at this point, visualize the constraints on screen by passing the constraints to the window using visualizeConstraints:.

    When you click a constraint, it is printed in the console. In this way you can you can determine which is which, and typically identify which is incorrect.

    At this point you may be informed that the layout is ambiguous.

  5. Find the code that’s wrong.

    Sometimes, once you have identified the incorrect constraint, you will know what to do to fix it.

    If this is not the case, then use Instruments to search for the pointer of the constraint (or some of its description). This will show you interesting events in that constraint’s lifecycle—its creation, modification, installation into windows, and so on. For each of these you can see the backtrace where it happened. Find the stage at which things went awry, and look at the backtrace. This is the code with the problem.

Auto Layout Degrades Gracefully with Unsatisfiable Constraints

It is a programming error to configure constraints that cannot be satisfied. Faced with unsatisfiable constraints, however, the Auto Layout system attempts to degrade gracefully.

  1. The addConstraint: method (or the setConstant: method of NSLayoutConstraint) logs the mutually unsatisfiable constraints and (because this is a programmer error) throws an exception.

  2. The system catches the exception immediately.

    Although adding a constraint that cannot be satisfied is a programmer error, it’s an error that one can readily imagine occurring on a user’s computer, and from which the system can recover more gracefully than crashing the program. The exception is thrown so that you notice the problem, and caught because it’s easier to debug the constraint system if the system does still function.

  3. To allow layout to proceed, the system selects a constraint from among the mutually unsatisfiable set and lowers its priority from “required” to an internal value that is not required, but that is higher priority than anything you can externally specify. The effect is that as things change going forward, this incorrectly broken constraint is the first that the system attempts to satisfy. (Note that every constraint in the set is required to begin with, because otherwise it wouldn’t be causing a problem.)

2010-08-30 03:48:18.589 ak_runner[22206:110b] Unable to simultaneously satisfy constraints:
(
    "<NSLayoutConstraint: 0x40082d820 H:[NSButton:0x4004ea720'OK']-(20)-|   (Names: '|':NSView:0x4004ea9a0 ) >",
    "<NSLayoutConstraint: 0x400821b40 H:[NSButton:0x4004ea720'OK']-(29)-|   (Names: '|':NSView:0x4004ea9a0 ) >"
)
 
Will attempt to recover by breaking constraint 
<NSLayoutConstraint: 0x40082d820 H:[NSButton:0x4004ea720'OK']-(20)-|   (Names: '|':NSView:0x4004ea9a0 ) >