UIKit Auto Layout

UIKit — setNeedsLayout vs layoutIfNeeded vs layoutSubviews()

Comprehensive guide on setNeedsLayout, layoutIfNeeded, and layoutSubviews()

Kanagasabapathy Rajkumar
4 min readDec 26, 2023

In the modern programming era, Developers have started building apps with the new Declarative Programming SwiftUI. But still, the UIKit exists and supports in SwiftUI application.

layoutSubViews()

Keith Harrison's Modern Auto Layout was instrumental in providing me with a deep understanding of the layout engine.

This article has been updated and moved to my website here

Layout Pass or Layout Cycle

The layout cycle is as follows:

Trigger — Changing the input to the layout engine. This can be achieved by adding or removing subviews, activating/deactivating constraints, Intrinsic Content Size, and Setting priorities and constants of a constraint.

Update Internal Model — The layout engine has an internal model of the size and position of each view and equation that describes the relationship between those views. Changing/Triggering the input will cause the layout engine to update its internal model and that solves the equations for new values in terms of size (width, height) and origin/position of view (x-axis and y-axis).

Deferred Layout Pass — Now the internal model has changed. It has two layout passes over the view hierarchy.

  • The first pass will allow updating any constraints
  • The second pass calls layoutSubViews() on each view allowing them to update the size and position of their subviews to match the internal model repositioning any views if needed.
Override in ViewController and View

Updating Constraints

Call setNeedsUpdateConstraints to request an update constraints pass for a view but it is deferred.

Call updateConstraintsIfNeeded method to update the internal model immediately.

Repositioning Views

The default behavior of layoutSubviews() sets the bounds and center of each subview to new values from the layout internal model.

layoutSubViews

Lays out subviews.

func layoutSubviews()

Subclasses can override this method as needed to perform a more precise layout of their subviews. We should override this method only if auto-resizing or constraint-based behaviors of the subviews do not offer the behavior you want.

To request a layout pass for a view, call setNeedsLayout or layoutIfNeeded (immediately).

setNeedsLayout

Invalidates the current layout of the receiver and triggers a layout update during the next update cycle.

func setNeedsLayout()

Returns immediately without updating the layout. Instead, it marks the layout of the view changed and schedules a deferred layout pass to run on the application run loop. Calls layoutSubviews during the next update cycle.

layoutIfNeeded

Lays out the subviews immediately, if layout updates are pending.

func layoutIfNeeded()

Calls layoutSubviews on the receiver if there are pending changes to force the layout engine to immediately update the size and position of subviews from the layout internal model. If no layout updates are pending, this method exits without modifying the layout or calling any layout-related callbacks.

Only call these methods on the main thread.

Call setNeedsLayout to manually schedule an update layout pass. Call layoutIfNeeded to force an immediate update to the view frames from the model.

layoutIfNeeded
setNeedsLayout

Example Usage

widthConstraint.constant = 100.0
let animator = UIViewPropertyAnimator(duration: 1.0, curve: .easeInOut) {
self.view?.layoutIfNeeded()
}
animator.startAnimation()

Points to Remember

  1. layoutIfNeeded is a synchronous call that will let the system update the views and force the layout engine to redraw the views.
  2. setNeedsLayout is a deferred call and asynchronous call that will mark the layout has changed but it will call layoutSubViews() in the next cycle.
  3. Both layoutIfNeeded and setNeedsLayout call layoutSubViews()
  4. Better to have setNeedsLayout before calling layoutIfNeeded for the safer side. But it is not required in all cases. Because layoutIfNeeded requires a signal to update.
  5. For animations, using layoutIfNeeded is a better option than calling setNeedsLayout. It ensures any pending layout updates are applied before the animation, resulting in smoother performance.
  6. The same logic applies to the setNeedsUpdateConstraints and updateConstraintsIfNeeded.

Grateful for your read, now let’s code on!

Interested in connecting? 
Feel free to connect with me on: LinkedIn and GitHub

Please take a look at my first-ever Article 👇

--

--

Kanagasabapathy Rajkumar
Kanagasabapathy Rajkumar

Written by Kanagasabapathy Rajkumar

Swift Enthusiast | Building Seamless iOS Experiences 🚀 | Swift & Objective-C |