Auto Layout
I’ve always taken for granted that whenever I make changes to constraints on a view, I’d expect the changes to get applied to the view. This chapter will look into when & how this happens. Let’s demystify auto layout.
The Layout Cycle
The application manages the layout cycle on its main run loop as follows:
- Handle events on the main dispatch queue
- Check for constraints changes
- If constraints have changed, schedule a deferred layout pass
Image from Presentation Slides, WWDC 2015, Session 219
Registering Constraint Changes
- Constraint changes are translated into mathematical expressions in the layout engine.
- Based on the constraint changes, the layout engine re-computes the dimensions & coordinates of the views & controls that need to be updated.
- These views get notified, and calls
superview.setNeedsLayout()
, causing the deferred layout pass to schedule.
Deferred Layout Pass
The deferred layout pass consists of 2 passes - the update pass & the layout pass.
The Update Pass:
The intention of the update pass is to register constraint changes for current layout pass. The layout engine traverses the view hierarchy, calling:
The Layout Pass:
Finally, the layout pass reposition affected views by assigning new dimensions & coordinates. In this pass, the layout engine traverses the view hierarchy, calling:
PreEmpting the Deferred Layout Pass
What can I do if I want to effect my layout changes right away instead of waiting until the next layout pass?
- Call
UIView
’ssetNeedsUpdateConstraints()
to trigger an update + layout pass - Call
UIView
’s’setNeedsLayout()
to trigger a layout pass
Wrapping Up
Constraint Updates:
updateConstraintsIfNeeded()
triggersupdateConstraints()
only if constraints need updatingsetNeedsUpdateConstraints()
triggersupdateConstraints()
unconditionally- Thus we don’t ever call
updateConstraints()
directly - we can override it to group constraint updates for performance.
Layout Updates: (ie, frame changes, view added, view resizes)
layoutIfNeeded()
triggerslayoutSubviews()
only if layout has changedsetNeedsLayout()
triggerslayoutSubviews()
unconditionally- Again, we don’t call
layoutSubview()
directly - we override it if autoresizing and constraint-based behaviors of the subviews do not offer the behavior you want.
Do Nots:
- Do not call
setNeedsUpdateConstraints()
inupdateConstraints
- this creates a feedback loop - Do not call
setNeedsLayout()
inlayoutSubview()
- this creates a feedback loop - Do not invalidate layout of views outside the view’s subtree