How willSizeThatFitsBlock, willLayoutSubviewsBlock, and didLayoutSubviewsBlock Operate in FrameLayoutKit
The willSizeThatFitsBlock, willLayoutSubviewsBlock, and didLayoutSubviewsBlock closures provide three deterministic lifecycle hooks in FrameLayoutKit that execute before sizing, before subview positioning, and after layout completion respectively, enabling precise intervention without subclassing.
FrameLayoutKit by kennic offers these optional callback properties on all FrameLayout-derived views to inject custom logic directly into the layout pipeline. Understanding exactly when each willSizeThatFitsBlock, willLayoutSubviewsBlock, and didLayoutSubviewsBlock fires—and what parameters they receive—allows you to modify constraints, adjust insets, or trigger animations at the precise moment needed.
Callback Lifecycle Overview
Each callback serves a distinct phase in the UIKit layout process. When assigned, these closures are stored as optional properties and invoked at specific points in FrameLayout.swift and its subclasses.
| Callback | Invocation Timing | Parameters | Primary Use Case |
|---|---|---|---|
| willSizeThatFitsBlock | First line of sizeThatFits(_:ignoreHiddenView:) |
(FrameLayout, CGSize) |
Adjust incoming size constraints or log measurements before calculation begins |
| willLayoutSubviewsBlock | Start of layoutSubviews(), before super.layoutSubviews() |
(FrameLayout) |
Modify edgeInsets, toggle visibility, or prepare auxiliary data before frame assignment |
| didLayoutSubviewsBlock | Inside defer block at end of layoutSubviews(), after all frames are set |
(FrameLayout) |
Trigger animations, notify delegates, or clean up temporary state after positioning |
All three properties default to nil and are declared as optional closure types in the source.
Implementation Details in FrameLayoutKit Source
The callbacks are defined in FrameLayout.swift and inherited by StackFrameLayout and ScrollStackView. The repository places the invocation points strategically to ensure deterministic execution order.
willSizeThatFitsBlock Invocation
In FrameLayoutKit/Classes/FrameLayout.swift, the willSizeThatFitsBlock fires immediately upon entering the sizing method, receiving the layout instance and the proposed size.
// FrameLayout.swift - sizeThatFits(_:ignoreHiddenView:)
willSizeThatFitsBlock?(self, size) // Called before any visibility checks
if isEmpty && ignoreHiddenView {
// sizing logic continues...
}
This hook executes before the view measures its content and before any visibility checks, making it ideal for capping maximum widths or logging performance metrics.
willLayoutSubviewsBlock Invocation
The willLayoutSubviewsBlock triggers at the very beginning of layoutSubviews() in FrameLayout.swift, prior to calling super.layoutSubviews() or calculating any frames.
// FrameLayout.swift - layoutSubviews()
willLayoutSubviewsBlock?(self) // Executed first
super.layoutSubviews()
// ... frame calculations follow
In StackFrameLayout.swift, this inherited behavior allows you to adjust stack-specific properties like spacing or alignment immediately before the layout engine positions subviews.
didLayoutSubviewsBlock Invocation
The didLayoutSubviewsBlock runs inside a defer block placed at the end of layoutSubviews(), guaranteeing execution after all frames are assigned and after optional skeleton-view handling completes.
// FrameLayout.swift - layoutSubviews()
willLayoutSubviewsBlock?(self)
super.layoutSubviews()
defer {
// skeleton handling occurs here
didLayoutSubviewsBlock?(self) // Final hook after all layout work
}
StackFrameLayout.swift implements an identical defer pattern, while ScrollStackView.swift inherits this behavior directly from FrameLayout, ensuring consistent post-layout callbacks across all layout containers.
Practical Code Examples
These closures support both direct property assignment and a fluent chainable API defined in Extensions/FrameLayout+Chainable.swift and Extensions/ScrollStackView+Chainable.swift.
Logging Incoming Size Constraints
Capture the exact size passed to sizeThatFits for debugging adaptive layouts.
let layout = FrameLayout { layout in
layout.willSizeThatFits { layout, targetSize in
print("🔍 willSizeThatFits – target size:", targetSize)
}
}
Adjusting Edge Insets Before Layout
Dynamically modify padding based on the view's current bounds before frames are calculated.
let stack = StackFrameLayout()
stack.willLayoutSubviews { layout in
if layout.bounds.width > 300 {
layout.edgeInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
} else {
layout.edgeInsets = .zero
}
}
Post-Layout Animation Triggers
Fade in content only after frames are fully determined to prevent animation artifacts.
let scroll = ScrollStackView()
scroll.didLayoutSubviews { layout in
UIView.animate(withDuration: 0.25) {
layout.alpha = 1.0
}
}
scroll.alpha = 0.0 // Start hidden until layout completes
Fluent Chainable API Usage
The chainable methods return self, enabling compact configuration while assigning the underlying blocks.
let card = FrameLayout()
.willSizeThatFits { layout, size in
layout.maxWidth = min(layout.maxWidth, 200)
}
.willLayoutSubviews { layout in
layout.translationX = 10
}
.didLayoutSubviews { layout in
print("✅ Layout done – final frame:", layout.frame)
}
Summary
willSizeThatFitsBlockexecutes at the start ofsizeThatFits(_:ignoreHiddenView:)inFrameLayout.swift, receiving the target size to adjust sizing logic before measurement occurs.willLayoutSubviewsBlockfires at the beginning oflayoutSubviews(), beforesuper.layoutSubviews()runs, allowing last-minute configuration of insets or visibility states.didLayoutSubviewsBlockruns inside adeferblock after all frames are assigned, providing a safe hook for animations and post-layout notifications.- All three callbacks are inherited by
StackFrameLayoutandScrollStackView, with chainable convenience methods available in theExtensions/directory.
Frequently Asked Questions
When should I use willSizeThatFitsBlock instead of willLayoutSubviewsBlock?
Use willSizeThatFitsBlock when you need to modify size constraints or inspect the proposed CGSize before the view calculates its content dimensions. Use willLayoutSubviewsBlock when adjusting properties that affect frame positioning—such as edgeInsets, translationX, or subview visibility—immediately before the layout engine assigns frames.
Are these callbacks available in StackFrameLayout and ScrollStackView?
Yes. Both StackFrameLayout and ScrollStackView inherit from FrameLayout, so they possess the same willSizeThatFitsBlock, willLayoutSubviewsBlock, and didLayoutSubviewsBlock properties. StackFrameLayout.swift additionally ensures didLayoutSubviewsBlock fires in its overridden layoutSubviews() method via the same defer pattern.
How do the chainable methods differ from direct property assignment?
The chainable methods defined in Extensions/FrameLayout+Chainable.swift and Extensions/ScrollStackView+Chainable.swift are simple wrappers that assign the closure to the underlying block property while returning self to enable fluent syntax. Functionally, layout.willLayoutSubviews { ... } is identical to layout.willLayoutSubviewsBlock = { ... }, but the chainable style improves readability when configuring multiple callbacks.
Can I safely modify view frames inside didLayoutSubviewsBlock?
While you can modify frames inside didLayoutSubviewsBlock, doing so may trigger another layout pass if the changes affect layout constraints. This callback executes after the layout engine finishes, making it ideal for animations, logging, or notifying delegates, but be cautious about mutating geometry to avoid performance penalties from redundant layout cycles.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →