# A first look at Auto Layout animation on iOS

Language: en
Canonical: [A first look at Auto Layout animation on iOS](<https://allluckly.cn/en/journal/2016-02-28-a-first-look-at-auto-layout-animation-on-ios/>)
中文：[中文 HTML](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tuogao25>)
English: [English HTML](<https://allluckly.cn/en/journal/2016-02-28-a-first-look-at-auto-layout-animation-on-ios/>)
Published: 2016-02-28T00:00:00+08:00
Modified: 2026-10-23T10:45:02+08:00
Author: [SindriLin](<http://sindrilin.com>)

Introduces constraints through text layouts in portrait and landscape, then breaks down an interface that animates as a list scrolls. Changing constraint values controls element positions and sizes, demonstrating the basics of combining Auto Layout with animation.

Originally credited to SindriLin; edited and archived by Bison.

[Source listed in the original](<http://sindrilin.com>)

English translation of the [Chinese original](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tuogao25>), translated on 2026-10-23. The article body uses automated translation; app descriptions, historical reading notes and video subtitles receive separate editorial checks. Original publication dates, attribution, code and version notes are retained.

**Before you read** This technical note dates from 2015–2017. The examples have not been rerun as part of this archive update. Read API, dependency and tool details in the context of the original publication date.

Editor: [Bison](<https://allluckly.cn/en/>)  
 Contributor: [SindriLin](<http://sindrilin.com>)

This constraint-animation article arrived later than expected because I could not find an animation I considered suitable—not because constraint animation is difficult. It is so simple that choosing a good simple demonstration was surprisingly hard. I recreated the following animation from a friend’s company app for today’s demo:

![1](<https://allluckly.cn/images/blog/tuogao/tougao25/1.gif>)

## About constraints

This section briefly introduces constraints. If you already develop with storyboards, you can skip it.

Suppose you need to centre an article on screen, with about 30pt on either side and a 20pt top margin, as shown:

![1](<https://allluckly.cn/images/blog/tuogao/tougao25/2.jpg>)

Thinking the task is trivial, you casually write:

```
self.textView = [[UITextView alloc] initWithFrame: CGRectMake(30, 20, self.view.bounds.size.width - 60, 0)];
self.textView.text = ..........
[self.textView sizeToFit];
```

Run the code: it looks good. But hold command and press the left or right arrow. The interface becomes:

![1](<https://allluckly.cn/images/blog/tuogao/tougao25/3.jpg>)

You quickly recognise the issue and observe the screen-orientation notification

```
UIApplicationWillChangeStatusBarOrientationNotification，
```

Then change the text width again in the callback:

```
- (void)notifyToAdjustTextView: (NSNotification *)notification
{
    switch ([UIDevice currentDevice].orientation) {
        case UIDeviceOrientationLandscapeLeft:
        case UIDeviceOrientationLandscapeRight:
            CGRect frame = self.textView.frame;
            frame.size.width = self.view.bounds.size.height - 60;
            self.textView.frame = frame;
            [self.textView sizeToFit];
        //more
        ......
    }
}
```

Run again. No matter how you rotate the screen, the text stays centred. Success! But that was a lot of work for one layout issue, which brings us to today’s subject: constraints.

A newly created project contains a `Main.storyboard`. Open it and add text directly to the controller Xcode created:

![1](<https://allluckly.cn/images/blog/tuogao/tougao25/4.jpg>)

Select the textView. Xcode provides the buttons below for adding constraints. Here I set the top spacing to 20pt and the left and right spacing to 30pt. Click `Add 3 Constraints` to finish adding them:

![1](<https://allluckly.cn/images/blog/tuogao/tougao25/5.jpg>)

Run the project and rotate the screen. The text always retains a 20pt top margin and 30pt side margins. Constraints simplify the work, do they not?

Every animation displays a sequence of calculations, including constraint animation. How are constraints calculated? In the basic demo above, adding constraints to the text control produces lines around textView in the storyboard, each representing a constraint. Double-click a line for more settings.

![1](<https://allluckly.cn/images/blog/tuogao/tougao25/6.jpg>)

For this constraint, all constraint calculations follow the equation:

TextView.Left = 1 \* superView.Left + 30

The parent view's left coordinate is calculated from 0pt, so the constraint relative to it is represented by `constant`. Changing `constant` and redrawing the interface can therefore produce animation.

For more Auto Layout instruction, see [this article](<http://www.cocoachina.com/ios/20141217/10669.html>)

## Animation analysis

Analysing the implementation before starting clarifies the animation. You can see that it depends on `tableView` scrolling condition, so observing `tableView` scroll offset is central to this animation. (*P.S. Why KVO rather than a delegate callback? When the tableView delegate and animation handler may be different objects, KVO is the safest choice.*)

From the user’s scrolling, we can identify the following effects:

- The navigation-bar title fades out with a colour transition.
- The navigation-bar title moves upward.
- The red background moves up and disappears.
- The countdown and text move upward into the navigation bar.

Combining this information, I defined the required macros:

```
#define OBSERVERKEY @"contentOffset"            ///<    监听属性
#define MAXMOVE 64.0                            ///<    滚动最大值
#define TITLEMAXMOVE 16.0                       ///<    导航文本位移最大值
#define TOPANDBOTTOMCONSTANT 9                  ///<    红色背景上下间距约束值
```

This is a parallax animation, using different view layers and displacements to create the visual effect. First write out the hierarchy of views that move; that helps implement it.

During the animation, the red background disappears after moving upward, so it must sit below the navigation bar's blue view. The countdown and text remain visible after moving upward, so they must be above the navigation view. The resulting order is text and countdown controls → navigation bar → red background. Moving each text and countdown control separately would require considerable code. Instead, put them all inside a transparent `UIView` and move that `UIView`. Below is Apple's view-hierarchy diagram for a navigation controller:

![1](<https://allluckly.cn/images/blog/tuogao/tougao25/7.jpg>)

Combining these conclusions, we know:

- The transparent view sits between `Custom view hierarchy` and `Navigation View`.
- The red background sits between `Navigation view` and `UIWindow`.

Because the animation controller is the demo's top-level screen, I chose a simple approach to save code: a `UIViewController` with a view pretending to be a navigation bar. To implement the animation on a second-level or deeper screen, you may need to subclass `UINavigationController` and customize the effect.

## Preparation

First build the interface in the storyboard. Drag a UIView from the lower-right controls panel. Its top must extend into the status-bar area; otherwise its top constraint starts below the status bar, placing it 20pt lower than intended.

![1](<https://allluckly.cn/images/blog/tuogao/tougao25/8.jpg>)

Next, drag a `UILabel` onto this navigation-bar view and connect it as `navigationTitle`. Set bottom, left, and right constraints to 0 and height to 44pt. Center the text and use white, 18-point system type.

![1](<https://allluckly.cn/images/blog/tuogao/tougao25/9.jpg>)

Most importantly, create a transparent view for the countdown and labels. I will not repeat adding views and constraints; here is the completed setup:

![1](<https://allluckly.cn/images/blog/tuogao/tougao25/10.jpg>)

Afterwards, reorder views in the left-hand hierarchy so the red background is below the navigation-bar view and the transparent content view is above it. Connect the key constraints to the file, including:

- The scrolling view: `tableView`.
- The centering constraint for the transparent view's child: `titleCenterX`.
- The transparent content view's top constraint: `contentTopConstraint`.
- The red background view's top constraint: `backgroundTopConstraint`.
- The imitation navigation title's bottom constraint: `labelBottomConstraint`.

## Building the animation

First, observe scrolling in the `tableView`. I usually place this code in `viewDidAppear:`, and remove observers or notifications in `viewDidDisappear:`.

```
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear: animated];
    [self.tableView addObserver: self forKeyPath: OBSERVERKEY options: NSKeyValueObservingOptionNew context: nil];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [self.tableView removeObserver: self forKeyPath: OBSERVERKEY];
}
```

As discussed earlier, the observer needs to obtain the scroll offset and calculate the percentage of the scrolling effect:

```
CGFloat yOffset = self.tableView.contentOffset.y / MAXMOVE;
yOffset = MAX(0, MIN(1, yOffset));
```

This value increases with scrolling until reaching 1, the final animation state. During this process, the navigation-bar title both moves upward and fades out:

```
self.navigationTitle.alpha = 1 - yOffset;
self.labelBottomConstraint.constant = yOffset * TITLEMAXMOVE;
```

Next, move the red background and countdown controls upward. Because all data-display controls belong to the transparent view, only that view needs to move:

```
CGFloat yPoint = TOPANDBOTTOMCONSTANT - yOffset * MAXMOVE;
self.backgroundTopConstraint.constant = self.contentTopConstraint.constant = yPoint;
```

All movement code is now complete. Run it and notice that the transparent view’s child controls seem to move a little too far upward.

Normally, a navigation bar is `44pt` high, with a `20pt` status bar above it, so I set the maximum movement to `44+20=64pt`. In the demo, after a `64pt` movement, the red background and transparent view reach the center of the `64pt` region. Since these two views are `45pt` high, `9.5pt` of their height extends into the status-bar area. To center the text at the navigation bar position during the animation, we therefore also need to move the text down by `9.5pt`. Add this line to the observation callback:

```
self.infoCenterX.constant = TOPANDBOTTOMCONSTANT * yOffset;
```

Add this line and run again; the animation is complete!

## Closing notes

I spent a long time planning this first post on constraint animation. This demo is closer to a `UIView` animation in that it changes coordinate values. Ultimately, I felt it would make the transition from `UIView` animation to constraint animation quicker. In the next post, I will explain using layout methods to animate constraint resets, as well as replacing and updating constraints during an animation.

[Demo for this article](<https://github.com/JustKeepRunning/LXDBaseAnimation>)

---

Recommended reading: [Preparing an iOS app for Apple Pay](<https://allluckly.cn/en/journal/2016-02-18-preparing-an-ios-app-for-apple-pay/>)

