Auto Layout by Example

Auto Layout makes it easy to solve many complex layout problems automatically, without the need for manual view manipulation. By creating the right combination of constraints, you can create layouts that are traditionally difficult to manage in code, such as equally spaced views that adjust to changes in orientation or size, elements inside scroll views that affect the size of the scrolling content, or elements inside scroll views that don’t scroll with the rest of the contents.

Using Scroll Views with Auto Layout

When you are creating an app using Auto Layout, scroll views can present a unique challenge. The size of the scrolling content must be set correctly so that the user can scroll through all of the available content, for example, and if you need to lock a contextual view in place on top of a scroll view, such as the scale and legend for a map, it’s difficult to ensure that the element doesn’t scroll with the rest of the content.

Controlling Scroll View Content Size

The size of the content inside of a scroll view is determined by the constraints of its descendants.

bullet
To set the size of a scroll view
  1. Create the scroll view.

  2. Place the UI element inside it.

  3. Create constraints that fully define the width and height of the scroll view content.

You must make sure you create constraints for all the subviews inside a scroll view. For example, when defining the constraints for a view that doesn’t have an intrinsic content size, you’ll need more than just a leading edge constraint—you must also create trailing edge, width, and height constraints. There cannot be any missing constraints, starting from one edge of the scroll view to the other.

Creating Anchored Views Inside a Scroll View

You may find you want to create an area inside a scroll view that doesn’t move when a user scrolls the contents of the scroll view. You accomplish this by using a separate container view.

bullet
To lock a view inside a scroll view
  1. Create a container view to hold the scroll view.

  2. Create the scroll view and place it in the container view with all edges equal to zero points.

  3. Create and place a subview inside of the scroll view.

  4. Create constraints from the subview to the container view.

The following example uses the steps in the above task to show how to position a text view inside of a scroll view. In this example, the text view stays at the bottom of the scroll view and doesn’t move when the scroll view contents are moved.

First, create the container view that will contain the scroll view. Set the size of the container view equal to the desired size of the scroll view.

../Art/sv_containerview.png

After the container view is created, create a scroll view and place it inside of the container view. Resize the scroll view so that all of the edges are flush with the container view’s edges, by setting the distance to 0.

../Art/sv_scrollview.png

Create another view and place it inside of the scroll view. In this example, a text view is placed inside of the scroll view.

../Art/sv_textview.png

After placing the text view, create constraints from the text view to the container view. Creating constraints that anchor the text view to the container view (skipping the scroll view) anchors the text view relative to the container view, which ensures that the scroll view won’t scroll the text view.

To create a constraint that crosses multiple views in the view hierarchy, it is generally easier to Control-drag from the view to the container view in the Interface Builder outline view.

../Art/sv_constraints.png

In the constraint overlay that appears, set the required constraints for the view.

../Art/sv_leadconstraint.png

In this example, constraints are created from the leading, trailing, and bottom edges of the text view to the container view. The height of the text view is also constrained.

../Art/sv_textviewconstraints.png

The following two figures show the app in iOS Simulator, both in normal and landscape positions. The text view is constrained at the bottom of the scroll view and doesn’t move as the scroll view is moved.

../Art/uiscrollview_bottom_sideways.png
../Art/uiscrollview_bottom_vertical.png

Spacing and Wrapping

Auto Layout provides several techniques for automatically spacing views and resizing items based on their content. The following sections describe how to create constraints that keep visible views proportionally spaced based on the orientation of the device.

Creating Equal Spacing Between Views

To lay out several views that are proportionally spaced based on the orientation of a device, create spacer views between the visible views. Set the constraints of these spacer views correctly to ensure that the visible views are able to stay spaced apart based on the orientation of the device.

bullet
To space views proportionally
  1. Create the visible views.

  2. Create the spacer views equal to the number of visible views plus one.

  3. Alternate placing your views, starting with a spacer view.

    To space two visible views, place all of the views in the following pattern, starting from the left side of the screen and moving right:

    spacer1 | view1 | spacer2 | view2 | spacer3.

  4. Constrain the spacer views so that their lengths are equal to each other.

  5. Create a leading constraint from the first spacer view to the container view.

  6. Create a trailing constraint from the last spacer view to the container view.

  7. Create constraints between the spacer views and the visible views.

The following example uses the steps in the above task to show how to position two views proportionally spaced. The spacer views are annotated for the example, but are normally left empty with no background. First, create the two views and place them in the storyboard.

../Art/es_twoviews.png

Add the three spacer views—one to the left of the leftmost view, one between the two views, and one to the right of the rightmost view. The spacer views don’t have to be the same size at this time because their size will be set through constraints.

../Art/es_spacersadded.png

Create the following constraints for the spacer views:

  • Constrain the width of spacer view 2 and spacer view 3 to be equal to the width of spacer view 1.

  • Constrain the width of spacer view 1 to be greater than or equal to the minimum desired width.

  • Create a Leading Space to Container constraint from spacer view 1 to the container.

  • Create a Horizontal Spacing constraint from spacer view 1 to view 1. Set this constraint to be a less-than-or-equal-to constraint with a priority of 1000.

  • Create Horizontal Spacing constraints from spacer view 2 to view 1 and view 2. Set these constraints to be a less-than-or-equal-to constraint with a priority of 999.

  • Create a Horizontal Spacing constraint from spacer view 3 to view 2. Set this constraint to be a less-than-or-equal-to constraint with a priority of 1000.

  • Create a Trailing Space to Container constraint from spacer view 3 to the container.

These constraints create two visible views and three invisible views (spacer views). These spacer views automatically resize as the orientation of the device changes, keeping the visible views proportionally spaced, as shown in the following two figures:

../Art/es_vertical.png
../Art/es_horizontal.png

Animating Changes Made by Auto Layout

If you need full control over animating changes made by Auto Layout, you must make your constraint changes programmatically. The basic concept is the same for both iOS and OS X, but there are a few minor differences.

In an iOS app, your code would look something like the following:

[containerView layoutIfNeeded]; // Ensures that all pending layout operations have been completed
[UIView animateWithDuration:1.0 animations:^{
     // Make all constraint changes here
     [containerView layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes
}];

In OS X, use the following code when using layer-backed animations:

[containterView layoutIfNeeded];
NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
     [context setAllowsImplicitAnimation: YES];
     // Make all constraint changes here
     [containerView layoutIfNeeded];
}];

When you aren’t using layer-backed animations, you must animate the constant using the constraint’s animator:

[[constraint animator] setConstant:42];