The Layout Engine в Swift
Many iOS developers do not think about how the mechanism for drawing elements, setting and updating constraints in Auto Layout works. In this article, I try to take a deep look inside the Layout Engine with the help of the Modern Auto Layout book.
The Layout Pass 🧐
The Layout Pass is when the Auto Layout Engine bypasses the view hierarchy but calls the ViewWillLayoutSubviews method on all viewControllers and the layoutSubviews method on all views. The layoutSubviews method updates the Frame property of each subview
When we add, change, or remove constraints, they don’t update right away. Recalculating the layout and updating the rendering on every change would be inefficient. Instead, your changes are scheduling window’s layout engine to trigger layout updates at the next opportunity in the runloop.
A typical layout cycle consists of several stages:
- Trigger: You change the input in the layout engine. This could be adding or removing subviews, changing the content’s internal size, activating/deactivating contstraints, or changing the priority or constant value of a constraint.
- Update Model: The layout engine has an internal model for the size and position of each view, and equations describing the relationships between those views. When the input changes, the layout engine updates its internal model and solves equations to get new size and position values for each view.
At the moment, only the internal model has changed. Views that now have a new size or position in the model call setNeedsLayout() on their superview, which schedules a delayed layout pass to run some time later in the runloop. - Deferred Layout Pass: When the layout pass is executed it runs two passes through the view hierarchy. The first pass gives you one last chance to update any constraints. The second pass finally calls layoutSubviews() on each view, allowing them to update the size and position of their subviews to match the internal model, moving any views as needed.
There are several methods that you can override on both the viewController and the view to interact with the layout engine during two layout passes:
- When updating the constraint pass, the layout engine calls the updateViewConstraints method of the view controller, and for the view it waits for the updateConstraints method to be updated.
- the layout pass signals the layout engine and it calls the viewWillLayoutSubviews and viewDidLayoutSubviews methods, as well as the layoutSubviews of all views that need to update the layout
Update Constraints 😈
The first pass through the view hierarchy is from the bottom up so you can change the constraints before the layout engine changes the position of the views. The layout engine calls updateConstraintsIfNeeded on each view to check for the latest constraints. For views marked as requiring their constraints to be updated, the layout engine calls their updateConstraints method, in which you can make changes. Call setNeedsUpdateConstraints to request that update constraints be passed to the view. If you want the layout engine to update its model, call the updateConstraintsIfNeeded method immediately.
Reposition Views ✍️
The second pass through the view hierarchy is a top-down transition for the view reposition. Only during this pass does the layout engine update the view frames according to its internal model.
For each view that requires a layout, the layoutSubviews() behavior defaults to setting the borders and center of each subviews to the new values from the layout engine model.
Call setNeedsLayout or layoutIfNeeded to request a layout pass for the view. The key difference between these methods is that the layout pass updates the view like this:
- setNeedsLayout: Returns immediately without updating the layout. Instead, it marks the layout in the view as changed and schedules a delayed layout pass to run in the application run loop.
- layoutIfNeeded: calls layoutSubviews on the receiver if there are pending changes to force the layout engine to immediately update the size and position of the subviews from its internal model
Conclusions 👀:
- Activating, changing, or deactivating constraints does not directly update view frames. Instead, it updates the layout engine’s model and schedules a layout pass that happens later in the application’s run cycle.
- There are two layout passes in the view hierarchy. The first pass allows you to update the constraints. The second pass updates the view layout, changing the size and position of the views to match the values from the layout engine.
- Call setNeedsLayout to manually schedule the layout update step. Calling layoutIfNeeded to force immediate update of view frames from the model.
Thank you for viewing the article.
Subscribe so you don’t miss anything ❤️👨🏻💻My links 🔗:Youtube 🎥: https://bit.ly/3eBDWM1Instagram 🖼: https://bit.ly/3Bb5DCZ