# How willSizeThatFitsBlock, willLayoutSubviewsBlock, and didLayoutSubviewsBlock Operate in FrameLayoutKit

> Discover how willSizeThatFitsBlock, willLayoutSubviewsBlock, and didLayoutSubviewsBlock provide precise lifecycle hooks in FrameLayoutKit for layout control without subclassing.

- Repository: [Nam Kennic/framelayoutkit](https://github.com/kennic/framelayoutkit)
- Tags: internals
- Published: 2026-03-05

---

**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`](https://github.com/kennic/framelayoutkit/blob/main/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`](https://github.com/kennic/framelayoutkit/blob/main/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`](https://github.com/kennic/framelayoutkit/blob/main/FrameLayoutKit/Classes/FrameLayout.swift), the `willSizeThatFitsBlock` fires immediately upon entering the sizing method, receiving the layout instance and the proposed size.

```swift
// 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`](https://github.com/kennic/framelayoutkit/blob/main/FrameLayout.swift), prior to calling `super.layoutSubviews()` or calculating any frames.

```swift
// FrameLayout.swift - layoutSubviews()
willLayoutSubviewsBlock?(self)          // Executed first
super.layoutSubviews()
// ... frame calculations follow

```

In [`StackFrameLayout.swift`](https://github.com/kennic/framelayoutkit/blob/main/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.

```swift
// FrameLayout.swift - layoutSubviews()
willLayoutSubviewsBlock?(self)
super.layoutSubviews()
defer {
    // skeleton handling occurs here
    didLayoutSubviewsBlock?(self)       // Final hook after all layout work
}

```

[`StackFrameLayout.swift`](https://github.com/kennic/framelayoutkit/blob/main/StackFrameLayout.swift) implements an identical defer pattern, while [`ScrollStackView.swift`](https://github.com/kennic/framelayoutkit/blob/main/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.

```swift
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.

```swift
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.

```swift
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.

```swift
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

- **`willSizeThatFitsBlock`** executes at the start of `sizeThatFits(_:ignoreHiddenView:)` in [`FrameLayout.swift`](https://github.com/kennic/framelayoutkit/blob/main/FrameLayout.swift), receiving the target size to adjust sizing logic before measurement occurs.
- **`willLayoutSubviewsBlock`** fires at the beginning of `layoutSubviews()`, before `super.layoutSubviews()` runs, allowing last-minute configuration of insets or visibility states.
- **`didLayoutSubviewsBlock`** runs inside a `defer` block after all frames are assigned, providing a safe hook for animations and post-layout notifications.
- All three callbacks are inherited by `StackFrameLayout` and `ScrollStackView`, with chainable convenience methods available in the `Extensions/` 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`](https://github.com/kennic/framelayoutkit/blob/main/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.