Hello! Welcome back.
In our last lesson, we focused on identifying dropped frames and diagnosing GPU-bound rendering issues like blending and off-screen rendering using the Core Animation instrument. We learned that to achieve a smooth 60 or 120 FPS, the entire process of preparing a frame must fit within a very tight budget.
Today, we shift our focus from the GPU to the CPU. A significant portion of that frame-preparation budget is consumed by the Layout pass, especially in apps with complex UIs built using Auto Layout. This lesson is dedicated to helping you diagnose and fix layout performance issues caused by complex constraint hierarchies. You will learn why Auto Layout can become a bottleneck, how to pinpoint slow layout calculations using Instruments, and what strategies you can use to optimize them.
The Cost of Auto Layout: From Constraints to Equations
Auto Layout is a powerful system, but it's not magic. Under the hood, it's a constraint solver. When you add constraints, the system translates them into a series of linear equations. It then solves this system of equations to determine the frame (position and size) of each view.
This process happens during specific phases of the Render Loop:
- Update Constraints: Your app can change constraints. This phase runs from the leaf-most views up to the window. You can hook into it by overriding
updateConstraints()in aUIViewsubclass. - Layout: The layout engine solves the equations and applies the results. This phase runs from the window down to the leaves. You can hook into it by overriding
layoutSubviews(). - Display: The views are drawn.
The work involved in solving these equations is the "cost" of Auto Layout. For most simple layouts, this cost is negligible. However, for complex hierarchies or when constraints are managed inefficiently, this process can become a major CPU bottleneck and cause hitches.
The most common and costly mistake is what's known as Constraint Churn: unnecessarily removing and re-adding constraints. To understand why this is so bad, let's explore the internals of Auto Layout.
High Performance Auto Layout - WWDC 2018
This article, a summary of a classic WWDC talk, provides an excellent mental model for Auto Layout performance. It explains the render loop, the concept of constraint churn, and what the layout engine is doing under the hood.
Please read the text associated with the following sections of the transcript: Start from the example code under the first image and read through the explanation of the Render Loop (Update Constraints, Layout, Display). Pay close attention to the analogy of churning constraints being like recreating all your subviews in layoutSubviews. Continue reading the section titled "So how do you fix it?" to see the correct way to handle one-time constraint setup. Finally, read the following section that "peel[s] back the covers" to understand how constraints are turned into equations for the engine. This will solidify why churning is so expensive.
As you've just read, every time you add or remove constraints, you are forcing the layout engine to redo work—re-evaluating equations, solving for variables, and tracking dependencies. When this happens repeatedly, for example on every layout pass, it's a recipe for poor performance.
Diagnosing Layout Bottlenecks with Time Profiler
So, how do you find out if Auto Layout is the cause of your app's hitches? The Time Profiler instrument is your primary tool. It samples your app's CPU activity, showing you exactly which functions are consuming the most time.
In the previous lesson, we used the Core Animation instrument to find when frames were dropped. Now, we'll use the Time Profiler to find why the main thread was too busy to meet the deadline.
The following video from Apple is a perfect walkthrough of using Instruments to diagnose a hang (a severe hitch) caused by an inefficient layout. It demonstrates the exact workflow you should use.
WWDC23: Analyze hangs with Instruments | Apple
This video shows how to use the Time Profiler and SwiftUI's View Body instrument to find and fix a synchronous hang caused by an inefficient Grid layout. The principles of identifying a busy main thread and finding the culprit are identical for both UIKit and SwiftUI.
Watch the video from 09:15 to 21:12. The key takeaways are: How to use the Hangs instrument to identify a problem area. How to zoom into the main thread in Time Profiler to see what it was busy doing. How to distinguish between a function being slow vs. being called too many times. The demonstrated fix, replacing an eager Grid with a LazyVGrid, is a direct example of solving a layout performance problem by doing less work upfront.
The workflow from the video is a powerful pattern for performance investigation:
- Reproduce the issue while profiling with Time Profiler.
- Isolate the time range where the main thread CPU usage is high.
- Analyze the Heaviest Stack Trace to see where the time is being spent. Key functions to look for are
layoutSubviews,updateConstraints, and anything related to adding or removing constraints. - Use the Call Tree options, especially Invert Call Tree and Hide System Libraries, to focus on your app's code.
Using Xcode's Built-in Tools
Alongside Instruments, Xcode provides other tools that are invaluable for understanding your layout's complexity.
Improving User Experience by Boosting Runtime Performance (Part 2)
This blog post from GetYourGuide's engineering team provides some excellent, practical advice on diagnosing and fixing constraint-related performance issues.
Please read the section titled "Constraints". It gives a great overview of using the Debug View Hierarchy and interpreting console warnings to find layout issues.
Debug View Hierarchy
You can access this at any time by pausing your app and clicking the "Debug View Hierarchy" button in the debug bar.

This tool is perfect for spotting two major problems:
- Overly Complex Hierarchies: Deeply nested views are more expensive for the layout engine to process. Seeing a dozen views nested inside each other just to achieve a simple visual effect is a huge red flag.
- Ambiguous or Conflicting Constraints: Xcode will display warning icons next to views with layout issues. While these are often considered correctness bugs, they are also performance bugs, as the engine must do extra work to break a constraint and resolve the conflict.
Fixing Layout Performance Issues
Once you've diagnosed a layout bottleneck, here are the most effective ways to fix it.
1. Avoid Constraint Churn
As we learned from the "High Performance Auto Layout" reading, the best fix is to stop re-doing work.
- Setup Once: Create constraints that don't change during the view's initialization and leave them alone.
- Activate/Deactivate: If a layout needs to change, don't remove all constraints. Instead, hold references to the different sets of constraints in arrays and use
NSLayoutConstraint.activate(_:)andNSLayoutConstraint.deactivate(_:)to toggle between them. This is far more efficient.
2. Simplify with UIStackView
Often, complex constraint hierarchies are the result of manually positioning a list of items. UIStackView is designed to solve this problem elegantly and efficiently. It automatically generates the necessary constraints to manage the position and distribution of its arranged subviews.

By using a UIStackView, you offload the complexity to the system, which can often manage it more efficiently than a large web of manual constraints. Furthermore, hiding and showing views within a stack view is highly optimized; simply set isHidden = true on an arranged subview, and the stack view automatically handles updating the layout for you.
3. Flatten Your View Hierarchy
Use the Debug View Hierarchy to look for "wrapper" views that don't serve a purpose other than containing another view. Each layer of hierarchy adds cost. Can two or three nested views be replaced by a single, more capable custom view? Flattening your hierarchy reduces the number of views and constraints the engine needs to process.
Conclusion
You now have a complete toolkit for tackling CPU-bound layout performance issues. You understand the theoretical cost of Auto Layout and have practical methods for both diagnosing and fixing the most common problems.
Key Takeaways:
- Auto Layout performance is determined by the complexity of the constraint system and how efficiently it's managed.
- Constraint Churn (unnecessarily removing and re-adding constraints) is a primary cause of layout bottlenecks.
- Use the Time Profiler to find CPU-heavy layout code, focusing on the heaviest stack trace with system libraries hidden.
- Use the Debug View Hierarchy to visually inspect for overly complex hierarchies and conflicting constraints.
- Fix layout issues by:
- Avoiding churn through one-time setup and activating/deactivating constraints.
- Simplifying layouts with
UIStackView. - Flattening unnecessary layers in your view hierarchy.
Preview of the Next Lesson:
We've now covered GPU and CPU performance issues related to rendering static views. However, many performance problems arise when dealing with dynamic content, like images downloaded from a network. In the next lesson, we will learn how to implement background image fetching, decoding, and caching to ensure smooth scrolling, a critical skill for building responsive, data-driven apps.