What is the Layout?
Layout’s job is to answer these two questions:
- What to draw?
- Where to draw?
The first question is easy to answer: Layout is responsible for a Layout view (UIView) and its subclasses. For the second question, Layout determines the frame of the Subview. Lay out the subviews on the screen according to their frames.
So how do you determine the frame of the subview?
- Manually set the frame size of the subview.
- Use constraints to describe frame relationships between subviews.
As the iPhone’s screen sizes expand, the first approach is limited. Therefore, the second method is commonly used: linear equations are used to describe the relationship between Constraint anchors. The Auto Layout engine uses this equation to calculate the frames of the subview. The equation format of linear equations is as follows:
width = trailing - leading
height = bottom - top
centerX = (leading + trailing) / 2
centerY = (top + bottom) / 2
Copy the code
In the case of known leading, top, trailing and bottom four anchors, the Auto Layout engine can calculate the frame of the subview through the above simple calculation.
It’s not hard to come to the conclusion that if a view knows its own frame, it can calculate the frames of its subviews using linear equations. So to recurse, as long as we know the frame of the root view, then the frame of the view can be solved by linear equations. The root view (UIWindow) determines the frame (screen size) at runtime.
The calculated frame data will be notified to the parent view of the corresponding view, and the parent view will layout the child view based on the data, that is, call the layoutsubViews method. The layoutsubViews method is called when a Layout Pass appears.
The Layout Cycle
Constraints Change
The constraints of a view are a collection of linear equations (inequalities) in the Auto Layout world. Changes to view constraints affect these linear equality (inequality) groups.
There are several general types of constraint changes:
- Activate a constraint
- Deactivate a constraint
- Change the priority of a constraint
- Modify the value corresponding to the constraint
- Adds or removes a view from the view hierarchy
The fifth point is easy to overlook.
Deferred Layout Pass
The Deferred Layout Pass can be divided into two parts:
- Update constraints
- Repositioning the view
The first step is to make sure that if there are changes to the constraints that are not in effect, they are in effect. The second step, which is relatively time consuming, is to read the Layout data from the Auto Layout engine and then iterate through the view hierarchy and update the Layout.
Assuming that without the first step, the rearrange the view step could be repeated multiple times, resulting in redundant calculations.
UpdateConstraints method uses a summary
In general, some time after the call setNeedsUpdateConstraints method, iOS will call ourselves updateConstraints method before layout implementation.
Do all custom constraint updates have to be placed in the updateConstraints method? Excerpts from some official document (developer.apple.com/documentati…
It is almost always cleaner and easier to update a constraint immediately after the affecting change has occurred. For example, if you want to change a constraint in response to a button tap, make that change directly in the button’s action method. You should only override this method when changing constraints in place is too slow, or when a view is producing a number of redundant changes.
Conclusion: Modifying view constraints in the updateConstraints method is often faster than elsewhere. But in general, view constraints need to be updated only when they need to be changed, which has the benefit of making your code more readable and maintainable.
However, when we need to modify many view constraints at the same time, using the above method can feel slow. At this point, you can choose to override the updateConstraints method. The Auto Layout engine can process these view constraint changes in batches at the same time to improve performance.
When the constraints of the view are changed, the Auto Layout engine recalculates the frame of the view. Then the view reads the frame data from the Auto Layout engine and calls superView.setNeedslayout () to update the interface. This process is called Deferred Layout Pass. The term Deferred indicates that a view’s frame does not change immediately after its constraint changes.
The layoutSubviews method uses a summary
Excerpts from some official document (developer.apple.com/documentati…
You should override this method only if the autoresizing and constraint-based behaviors of the subviews do not offer the behavior you want. You can use your implementation to set the frame rectangles of your subviews directly.
By overwriting the layoutSubviews method, we can add code for our custom layout. One caveat: You only really need to do this if a layout cannot be implemented by setting constraints.
When the layoutSubViews method is called, the layout is in progress, that is, some views have been laid out and some views have not been laid out or are about to be laid out.
When covering layoutsubViews method, can’t call setNeedsUpdateConstraints method, which can easily lead to Layout Feedback loops (Layout Feedback Loop). This article (www.hackingwithswift.com/articles/59…
Auto Layout Debug tips
There are two common problems with Auto Layout:
- Unsatisfiable Constraints -> The system has no solution
- Ambiguous Layouts -> Linear equations have multiple solutions
Unsatisfiable Constraints
In this case, the Auto Layout engine can actively select the broken constraint as a breakthrough point to investigate exactly where the conflict occurred. It can also add identifier and accessibilityIdentifier to the suspect constraint and view to improve the readability of the debug log.
Ambiguous Layouts
Ambiguous Layouts can be found in:
- The view has too few constraints to calculate a definite frame.
- The priorities of view constraints conflict. Procedure
When this happens, a view calculates a frame with several possible results, and the Auto Layout engine selects one of them to complete the Layout, which is often not the ideal Layout.
We can use:po view.value(forKey: "_autolayoutTrace")!
Printing out the current view hierarchy makes it easy to see which subview is experiencing ambiguous layouts problems. We can even continue to executee label.exerciseAmbiguityInLayout()
And then executecontinue
Command to see other possible layouts. This makes it easier to find the problem by comparing the two layouts.
High Performance Auto Layout
Let’s look at this simple example:Such a simple layout does not require much effort to coverupdateConstraints
The method. Here’s a counter example to better understand the Auto Layout black box.In this method, you first deactivate existing constraints, then re-add constraints, and finally enable newly added constraints. Intuitively, there seems to be no loss of performance.
Render Loop
To understand the performance impact of the above code, we need to be more specificupdateConstraints
A new concept is introduced here:Render Loop
.Render Loop is a process that may run 120 times per second to ensure that each subview is properly displayed on the screen. It consists of three phases: Update Constraints, Layout, and Display.
Phase 1: Each view is called from the bottom of the view hierarchy to the topupdateConstraints
Methods. Stage 2: From top to bottom of the view hierarchy, each view is calledlayoutsubViews
Methods. Stage 3: From top to bottom of the view hierarchy, each view determines whether it needs to be displayed on the screen.
These methods are listed in this way in order to show that these methods are at different stages, but can beUsed by analogy.
The entire Render Loop exists for:
- Avoid useless calculations.
- Postpone some computation (so that it can be done centrally at some point).
- If possible, skip these calculations.
Look back at this code:Mentioned aboveupdateConstraints
The method can be the samelayoutSubviews
Method analogy. inlayoutSubviews
Each time the method is called, all child views are destroyed and then recreated and added to the superview. When most people look at the code above, they have a gut feeling that this affects performance.
The same goes for that updateConstraints method!
We just have to find a way to make sureupdateConstraints
The actions within the method are executed only once to avoid redundant calculations.
Using the analogy above, it becomes clear that the code in the updateConstraints method above is code that needs to be optimized. But our goal is not just to show that this code is bad, but to try to understand what’s behind it.
What happens behind Activate Constraints?
After you add a constraint to the view, the Auto Layout engine calculates the equations that represent the constraint and tells the view the solution to the equations (the view’s frame data, minX, minY, width, and height).
Take the simple layout shown above (focus only on the horizontal layout). The Auto Layout engine starts to compute a simple set of equations:
When the calculation is complete, the Auto Layout engine notifies the view, which then sends the setNeedsLayout message to its parent.
Now enter Render Loop stage 2 Layout:
inlayoutSubViews
Method, the view reads the solution of the equations (frame data) from the Auto Layout engine to Layout the subview.
So far, let’s take a step-by-step journey through the bureau. You now have an intuitive understanding of this process, which will help you make appropriate judgments about the performance of your current layout.
Let’s review this code, which deactivates the existing constraints and then enables the newly added ones in a situation where the Render Loop might be calling the updateConstraints method 120 times per second. The Auto Layout engine solves the same equations 120 times a second. Although the Auto Layout engine will solve a system of equations quickly, but accumulated down here will produce a lot of overhead, will have a small impact on performance!
In most cases, we are adding constraints to child views within the same parent view.
As shown above, Text1 and text2 have the same parent view, and Text3 and text4 have the same parent view. Text1 and Text3 are in two different view hierarchies.
The time complexity of the Layout is linear because the Auto Layout engine is actually solving two independent equations.
Text1 and text3 are left aligned:The Auto Layout engine needs to solve two dependent equations. As you can imagine, the time complexity is no longer linear (need to confirm again), and the calculation amount will be larger than before.
From the above explanation, one thing can be gradually comprehended:
The layout engine is a layout cache and dependency tracker
Light on the Auto Layout this black box has a more intuitive cognition is not enough, also need through the actual concrete examples to reinforce the cognition, strongly recommend watching the High Performance Auto Layout (developer.apple.com/videos/play… Examples of.
translatesAutoresizingMaskIntoConstraints
From the Interface Builder to generate UIView translatesAutoresizingMaskIntoConstraints (after referred to as “tAMIC) as the default is false, The tAMIC of the code generated UIView defaults to true.
Manually set the Layout of the frame view and use Auto Layout behind the scenes.
For example, add the following code to the viewDidLoad method:
label.translatesAutoresizingMaskIntoConstraints = true
view.addSubview(label)
label.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
Copy the code
The interrupt point enters the debugging state and checks the constraint of the label:As you can see, the manually set frame is secretly converted to the corresponding frameNSAutoresizingMaskLayoutConstraint
To do the layout.
When creating view constraints in code, be sure to set tAMIC to false to avoid constraint collisions.
Alignment Rect
“The Auto Layout engine calculates the data of the frame view.” This is often a mistake of many people. The Auto Layout engine calculates the data for the view Alignment Rectangle. In other words, Auto Layout uses an Alignment Rectangle for Layout instead of a frame.
However, generally speaking, the view Alignment rectangle and Frame have the same data.
The yellow dotted lines in the figure above indicate the range of two views. The difference between an Alignment Rectangle and a Frame rectangle is that the former contains only the view’s core visual elements, while the latter contains not only the view’s core visual elements, but also other decorative elements. Uiview. transform does not change the size of the Alignment Rectangle.
For example, here is an image with a width of 180 x 80 and a green rectangle with a size of 150 x 50.If you want this image to be centered, the layout code is:
imageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(imageView)
NSLayoutConstraint.activate([
imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
Copy the code
But if you want to center the green rectangle with reference to its center, you need to change the UIImageView Alignment Rectangle.
private let dogImageView = UIImageView(named: "alignment_rect", top: 0, left: 0, bottom: 30, right: 30)!
.
.
extension UIImageView {
convenience init?(named name: String.top: CGFloat.left: CGFloat.bottom: CGFloat.right: CGFloat) {
guard let image = UIImage(named: name) else {
return nil
}
let insets = UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)
let insetImage = image.withAlignmentRectInsets(insets)
self.init(image: insetImage)
}
}
Copy the code
So far, you can prove that “Auto Layout uses an Alignment Rectangle for Layout instead of a frame.”
Intrinsic Content Size
Common UI controls such as UILabel and UIImageView. After setting their text and images to display, they have intrinsicContentSize, Auto Layout engine NSContentSizeLayoutConstraint generated by this intrinsicContentSize Layout on them.
Optionally overriding the intrinsicContentSize can improve layout performance when Text measurement is required. The performance overhead of Text measurement can be significant, especially in apps that display large amounts of Text.
If the size of the Text display can be determined without Text Measurement, the intrinsicContentSize can be overridden directly to return the defined value.
override var intrinsicContentSize: CGSize {
// Return a certain CGSize
return CGSize(width: 100, height: 100)}Copy the code
If you use a constraint to determine the size of the text display rather than the text itself, you can do this:
override var intrinsicContentSize: CGSize {
return CGSize(width: UIView.noIntrinsicMetric, height: UIView.noIntrinsicMetric)
}
Copy the code
This tells the superview that I can already determine my own size by constraint, and there is no need for Text measurement.
Speaking of intrinsicContentSize, it is necessary to mention the systemLayoutSizeFitting method, as the two are sometimes confused, in fact the two can be said to be pros and cons. IntrinsicContentSize is the information passed to the Auto Layout engine while the systemLayoutSizeFitting method is the information calculated by the Auto Layout engine.
The reference information
- Mysteries of Auto Layout, Part 1 (developer.apple.com/videos/play…
- Mysteries of Auto Layout, Part 2 (developer.apple.com/videos/play…
- High Performance Auto Layout (developer.apple.com/videos/play…