Animating Autolayout Constraints

Updated 23 May 2015: I reworked this code a little from the original posting based on feedback to avoid adding and removing constraints at runtime. Instead I now animate changing the relative priorities of the yellow and blue view constraints which is a lot simpler as well as being more efficient.

Before autolayout if you wanted to move a view on screen you would manipulate the frame of the view to change either the origin or size. The frame along with the bounds and center are among the properties of UIView that you can animate so that the user sees the changes happen over a short period of time.

If you start using autolayout you quickly learn that you should not directly change the frame (or bounds or center) of a view. Instead you need to change the autolayout constraints. This post walks through a simple example showing how you can achieve the same view animation effects using autolayout.

The Challenge

To keep it simple we will use just two views - a yellow view and a blue view. In “normal” mode only the yellow view should be visible. In “fancy” mode both the yellow and blue views should be visible. The views should fill the screen apart from the standard margins at the edge of the device and space for a switch that toggles between modes. The animated gif below shows what we want to achieve.

The blue view should slide into and out of the view from the right and the yellow view should resize to fit.

Setting up the base constraints

To get started I will setup the views and autolayout constraints in Interface Builder for the situation when both views are onscreen.

The yellow view has five constraints: a leading space constraint to the superview, a trailing space constraint to the blue view, top and bottom spacing to the switch and bottom layout guide and finally equal width to the blue view.

The blue view has a similar setup with five constraints except that it has a trailing space constraint to the superview:

Nonrequired Priorities

For the situation where only the yellow view is visible it also needs a trailing space constraint to the superview. If I add that constraint it will conflict with the blue trailing constraint as they will both have a priority of 1000. To avoid the conflict and to move the blue view on and offscreen we can change the relative priorities of the trailing space constraints for the yellow and blue views.

Required constraints have a priority of UILayoutPriorityRequired (1000). You cannot change the priority of a required constraint at runtime. A priority less than UILayoutPriorityRequired is an optional or nonrequired constraint. You can change the priority of a nonrequired constraint at runtime as long as you do not set it to UILayoutPriorityRequired.

So first we can adjust the priority of the blue trailing space constraint to set the priority to High (750).

We can then also add a trailing space constraint from the yellow view to the superview and also set its priority to High (750).

Creating Outlets for the Constraints

To be able to change the priority of the blue trailing space constraint at runtime we need to set up an outlet in our view controller connected to the constraint in the storyboard. You can create an outlet to a constraint the same way you create outlets for any other object in Interface Builder by control dragging from the constraint into the view controller.

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *blueViewConstraint;

To be sure we push the blue view offscreen we also need to be able to adjust the spacing between the views. So we will also create an outlet in our view controller for the yellow-blue horizontal spacing constraint:

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *viewSpacingContraint;

Updating the constraints

It is now easy to write a method to set the correct priority for the blue constraint depending on the mode switch:

- (void)updateConstraintsForMode {
  if (self.modeSwitch.isOn) {
    self.viewSpacingContraint.constant = 8.0;
    self.blueViewConstraint.priority = UILayoutPriorityDefaultHigh+1;
  } else {
    self.viewSpacingContraint.constant = self.view.frame.size.width;
    self.blueViewConstraint.priority = UILayoutPriorityDefaultHigh-1;

We set the priority of the yellow trailing space constraint to UILayoutPriorityDefaultHigh in the storyboard. To make the blue view visible we need the priority of its trailing space constraint to be some value higher than UILayoutPriorityDefaultHigh. To hide the blue view we set the priority to a lower value.

Note that we also set the width between the two views to some arbitrary large value (in this case I use the width of the superview) when we want to be sure that the blue view is off screen to push it beyond the right margin.

We should also configure the constraints when the view first loads:

- (void)viewDidLoad {
  // ...
  [self updateConstraintsForMode];

Animating the changes

We now have everything we need to animate the changes when the user switches mode. The Apple Auto Layout Guide describes the basic approach to animating changes made by auto layout. The recommended code snippet for iOS is as follows:

[containerView layoutIfNeeded];
[UIView animateWithDuration:1.0 animations:^{
  // Make all constraint changes here
  [containerView layoutIfNeeded];

The two calls to layoutIfNeeded on the container view force any pending operations to finish and then in the animation block capture the frame changes. Applying this approach to our example when the mode switch changes we have:

- (IBAction)enableMode:(UISwitch *)sender {
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  [defaults setBool:sender.isOn forKey:modeUserDefaultKey];
  [defaults synchronize];

  [self.view layoutIfNeeded];
  [UIView animateWithDuration:1.0 animations:^{
    [self updateConstraintsForMode];
    [self.view layoutIfNeeded];

Wrapping Up

You can find the full AnimatedConstraints example Xcode project from this article in my Coding Examples GitHub repository.

Modern Auto Layout

Want to learn more about Auto Layout? Be sure to check out my book: