# How SectionKit Manages View Lifecycle for Cells: A Deep Dive into iOS Collection Views

> Learn how SectionKit manages view lifecycle for cells in iOS Collection Views. Discover its layered approach for cell creation, configuration, and reuse.

- Repository: [神奇/sectionkit](https://github.com/linhay/sectionkit)
- Tags: deep-dive
- Published: 2026-03-06

---

**SectionKit decouples cell creation, configuration, display, and reuse into three coordinated layers—cell wrappers, section implementations, and delegate forwarding—to provide granular lifecycle hooks while isolating view-specific logic from UIKit's collection view mechanics.**

In the `linhay/sectionkit` repository, view lifecycle management follows a protocol-oriented architecture that separates concerns between the `UICollectionViewCell` wrapper, the section-level data owner, and the delegate forwarding bridge. This design allows developers to intercept and customize behavior at precise moments—such as `willDisplay` and `didEndDisplaying`—without subclassing UIKit components directly.

## The Three-Layer Architecture

SectionKit organizes cell lifecycle management across three distinct layers that communicate through protocol contracts:

- **Cell Wrapper Layer**: The concrete `SKCWrapperCell<View>` class instantiates and hosts your custom view.
- **Section Implementation Layer**: `SKCSingleTypeSection<Cell>` owns the data array and lifecycle action hooks.
- **Delegate Forwarding Layer**: `SKCDelegateForward` bridges UIKit's callbacks to the appropriate section.

### Cell Wrapper Layer (SKCWrapperCell)

The `SKCWrapperCell` class in [`Sources/SectionUI/Sections/SKCWrapperCell.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionUI/Sections/SKCWrapperCell.swift) serves as a generic container that conforms to both `SKConfigurableView` and `SKLoadViewProtocol`. It lazily instantiates the wrapped view during initialization and forwards configuration calls:

```swift
open class SKCWrapperCell<View: SKConfigurableView & SKLoadViewProtocol>: UICollectionViewCell,
                                                               SKConfigurableView,
                                                               SKLoadViewProtocol {
    // Preferred size delegation
    public static func preferredSize(limit size: CGSize, model: View.Model?) -> CGSize {
        View.preferredSize(limit: size, model: model)
    }

    // Forward model to wrapped view (lines 35-37)
    open func config(_ model: View.Model) {
        wrappedView.config(model)
    }

    // Lazy view instantiation (lines 39-45)
    public private(set) lazy var wrappedView: View = {
        if let nib = View.nib {
            return nib.instantiate(withOwner: nil, options: nil).first as! View
        } else {
            return View()
        }
    }()
}

```

This wrapper isolates view-specific logic—including layout calculations and model handling—from the cell lifecycle events managed by `UICollectionView`.

### Section Implementation Layer (SKCSingleTypeSection)

The `SKCSingleTypeSection` class in [`Sources/SectionKit/CollectionSingleTypeSection/Entities/SKCSingleTypeSection.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/CollectionSingleTypeSection/Entities/SKCSingleTypeSection.swift) implements the `SKCDelegateProtocol` and transforms UIKit delegate calls into type-safe actions. It provides the primary hooks for lifecycle observation:

```swift
open func item(willDisplay view: UICollectionViewCell, row: Int) {
    // Fire .willDisplay action (lines 25-27)
    sendAction(.willDisplay, view: view as? Cell, row: row)
    // Track display metrics
    displayedTimes.update(by: row)
}

open func item(didEndDisplaying view: UICollectionViewCell, row: Int) {
    // Fire .didEndDisplay action (lines 30-32)
    sendDeleteAction(.didEndDisplay, view: view as? Cell, row: row)
}

```

Developers register callbacks using the fluent API. For example, in [`Example/Foundation/HelloWorldViewController.swift`](https://github.com/linhay/sectionkit/blob/main/Example/Foundation/HelloWorldViewController.swift) at line 111:

```swift
section.onCellAction(.willDisplay) { context in
    context.view()?.backgroundColor = .systemYellow
}

```

### Delegate Forwarding Layer (SKCDelegateForward)

`SKCDelegateForward` in [`Sources/SectionKit/CollectionBaseProtocol/SKCDelegate/SKCDelegateForward.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/CollectionBaseProtocol/SKCDelegate/SKCDelegateForward.swift) intercepts UIKit's collection view delegate methods and routes them to the owning section. This bridges the gap between UIKit's index-path-based API and SectionKit's section-centric architecture:

```swift
@available(iOS 8.0, *)
func collectionView(_ collectionView: UICollectionView,
                    willDisplay cell: UICollectionViewCell,
                    forItemAt indexPath: IndexPath) {
    let value: Void = find { $0.collectionView(collectionView,
                    willDisplay: cell, forItemAt: indexPath) }
    observe { item in
        item.collectionView(collectionView,
            willDisplay: cell, forItemAt: indexPath, value: value)
    }
}

```

The forwarder extracts the section responsible for the index path and invokes `item(willDisplay:row:)` (lines 96-100). An identical pattern handles `didEndDisplaying` and supplementary view lifecycle events.

## Step-by-Step Lifecycle Flow

Understanding the exact sequence helps debug performance issues and optimize cell configuration.

### Cell Creation

When `UICollectionView` requests a cell via dequeuing, the framework returns an instance of `SKCWrapperCell<View>`. The wrapper lazily creates the underlying view through its `wrappedView` property, supporting both programmatic initialization and nib-based loading via the `SKLoadViewProtocol` interface.

### Configuration

After dequeuing, SectionKit calls `config(_:)` on the wrapper cell, which immediately forwards the model to `wrappedView.config(model)`. This occurs before the cell enters the visible bounds, ensuring the view is fully configured when lifecycle display events fire.

### Will-Display Events

As the cell approaches the visible area, UIKit calls `collectionView(_:willDisplay:forItemAt:)`. The `SKCDelegateForward` receives this callback and identifies the appropriate section. The section then executes `item(willDisplay:row:)`, which:

1. Increments the display counter via `displayedTimes.update(by: row)`
2. Fires the `.willDisplay` action to any registered observers

### Did-End-Displaying Events

When the cell scrolls off-screen, the reverse sequence occurs. UIKit triggers `didEndDisplaying`, `SKCDelegateForward` routes to the section, and `item(didEndDisplaying:row:)` invokes `sendDeleteAction(.didEndDisplay, view:row:)`. This signals that the cell is no longer visible and can release heavy resources or stop animations.

### Reuse Handling

SectionKit does not override `prepareForReuse()` in the wrapper cell. The standard `UICollectionViewCell` reuse mechanism applies automatically. Developers requiring custom cleanup logic should implement `prepareForReuse()` in their specific `View` type rather than the wrapper.

## Practical Implementation Example

The following example demonstrates the complete lifecycle integration:

```swift
// 1. Define a configurable view
final class MyItemView: UIView, SKConfigurableView, SKLoadViewProtocol {
    struct Model { let title: String }
    func config(_ model: Model) { 
        // Configure UI elements
    }
}

// 2. Create a typed section
let section = SKCSingleTypeSection<MyItemView>()
section.models = ["One", "Two", "Three"].map { MyItemView.Model(title: $0) }

// 3. Attach lifecycle observers
section.onCellAction(.willDisplay) { ctx in
    ctx.view()?.backgroundColor = .systemGreen
}

section.onCellAction(.didEndDisplay) { ctx in
    // Cleanup resources
}

// 4. Append to collection view
collectionView.manager.append(section)

```

When scrolling occurs, the system automatically handles the transition from cell creation through the display callbacks without manual delegate conformance in the view controller.

## Summary

SectionKit's view lifecycle management relies on strict separation of concerns:

- **Cell wrappers** (`SKCWrapperCell`) handle view instantiation and model forwarding in [`Sources/SectionUI/Sections/SKCWrapperCell.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionUI/Sections/SKCWrapperCell.swift)
- **Section implementations** (`SKCSingleTypeSection`) provide type-safe lifecycle hooks at lines 25-32 of [`Sources/SectionKit/CollectionSingleTypeSection/Entities/SKCSingleTypeSection.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/CollectionSingleTypeSection/Entities/SKCSingleTypeSection.swift)
- **Delegate forwarding** (`SKCDelegateForward`) bridges UIKit callbacks at lines 96-100 of [`Sources/SectionKit/CollectionBaseProtocol/SKCDelegate/SKCDelegateForward.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/CollectionBaseProtocol/SKCDelegate/SKCDelegateForward.swift)

This architecture allows precise control over `.willDisplay` and `.didEndDisplay` events while maintaining clean separation between data logic and view presentation.

## Frequently Asked Questions

### How do I detect when a cell appears on screen in SectionKit?

Register a callback using `section.onCellAction(.willDisplay)` on your `SKCSingleTypeSection` instance. This executes immediately before the cell becomes visible, triggered by the `item(willDisplay:row:)` method forwarding the UIKit delegate call through `SKCDelegateForward`.

### Where should I implement custom reuse preparation logic?

Implement `prepareForReuse()` in your custom view class that conforms to `SKConfigurableView`, not in the `SKCWrapperCell`. The wrapper intentionally does not override this method, allowing the standard `UICollectionViewCell` reuse cycle to function normally while your view handles specific cleanup.

### Can I access the index path inside lifecycle callbacks?

The callback context provides the row through the `Context` object. While the section knows the row index via the `row` parameter passed to `item(willDisplay:row:)`, the action system abstracts index paths into section-relative coordinates to maintain consistency across different collection view layouts.

### Does SectionKit support lifecycle hooks for supplementary views?

Yes. `SKCDelegateForward` implements symmetric forwarding for supplementary views through methods like `willDisplaySupplementaryView` and `didEndDisplayingSupplementaryView`, routing these to the appropriate section implementations using the same pattern as cell lifecycle management.