# How to Handle Different Cell Types Within the Same Section in SectionKit

> Learn how to handle different cell types in the same SectionKit section. Use enums and switch statements to dequeue heterogeneous cells efficiently.

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

---

**Use an enum to represent each cell type and switch on it inside `itemSize(at:)` and `item(at:)` to dequeue heterogeneous cells while conforming to `SKCSectionProtocol` and `SKCSectionActionProtocol`.**

SectionKit decouples section logic from cell management through protocol-oriented design. To display multiple cell types within a single section, you leverage `SKCSectionProtocol` for lifecycle hooks and `SKCSectionActionProtocol` for registration and dequeuing helpers. This pattern keeps your code type-safe and declarative while allowing any mixture of headers, items, and footers inside one section.

## The Architecture Behind Heterogeneous Sections

SectionKit separates responsibilities across two core protocols. `SKCSectionProtocol`, defined in [`Sources/SectionKit/CollectionBaseProtocol/SKCSectionProtocol.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/CollectionBaseProtocol/SKCSectionProtocol.swift), binds a section to the manager and mandates methods for item count, sizing, and cell provision. `SKCSectionActionProtocol`, found in [`Sources/SectionUI/Sections/SKCSectionActionProtocol.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionUI/Sections/SKCSectionActionProtocol.swift), exposes convenience methods such as `register(_:)` for cell registration and `dequeue(at:)` for type-safe cell retrieval.

When you conform to both protocols, the manager automatically calls `config(sectionView:)` when the section is added to the collection view. This is where you register every cell class the section will display.

## Step-by-Step Implementation

The following workflow demonstrates the pattern used in [`MixedCellsSectionTemplate.swift`](https://github.com/linhay/sectionkit/blob/main/MixedCellsSectionTemplate.swift) to mix headers, items, and footers in one section.

### 1. Define an Enum for Cell Types

Create an enum where each case carries the model required by its corresponding cell. This acts as the single source of truth for the section’s data source.

```swift
enum CellType {
    case header(HeaderCell.Model)
    case item(ItemCell.Model)
    case footer(FooterCell.Model)
}

```

Store an array of this enum as a property on your section class. The `itemCount` property returns the count of this array.

### 2. Register Cell Classes in config(sectionView:)

Implement `config(sectionView:)` to register all cell classes with the collection view. The manager invokes this method automatically when the section is attached.

```swift
func config(sectionView: UICollectionView) {
    register(HeaderCell.self)
    register(ItemCell.self)
    register(FooterCell.self)
}

```

The `register(_:)` method is provided by `SKCSectionActionProtocol` and handles the low-level `register(_:forCellWithReuseIdentifier:)` call on the underlying `UICollectionView`.

### 3. Calculate Sizes with itemSize(at:)

Switch on the enum to compute sizes dynamically based on the cell type at a specific index.

```swift
func itemSize(at row: Int) -> CGSize {
    switch cellTypes[row] {
    case .header(let model):
        return HeaderCell.preferredSize(limit: safeSizeProvider.size, model: model)
    case .item(let model):
        return ItemCell.preferredSize(limit: safeSizeProvider.size, model: model)
    case .footer(let model):
        return FooterCell.preferredSize(limit: safeSizeProvider.size, model: model)
    }
}

```

### 4. Dequeue and Configure Cells in item(at:)

Use the same switch pattern to dequeue the correct cell type and inject its model.

```swift
func item(at row: Int) -> UICollectionViewCell {
    switch cellTypes[row] {
    case .header(let model):
        let cell = dequeue(at: row) as HeaderCell
        cell.config(model)
        return cell
    case .item(let model):
        let cell = dequeue(at: row) as ItemCell
        cell.config(model)
        return cell
    case .footer(let model):
        let cell = dequeue(at: row) as FooterCell
        cell.config(model)
        return cell
    }
}

```

The `dequeue(at:)` helper returns a fully typed instance, eliminating the need for manual casting.

### 5. Handle Selection (Optional)

Implement `item(selected:)` to react to taps based on the cell type at the selected index.

```swift
func item(selected row: Int) {
    if case .item(let model) = cellTypes[row] {
        print("Item tapped: \(model.title)")
    }
}

```

## Complete Working Example

Below is the full implementation from [`.agent/skills/sectionui/examples/MixedCellsSectionTemplate.swift`](https://github.com/linhay/sectionkit/blob/main/.agent/skills/sectionui/examples/MixedCellsSectionTemplate.swift), demonstrating a section that accepts headers, items, and footers.

```swift
import UIKit
import SectionKit
import SectionUI

final class MixedCellsSectionTemplate: SKCSectionProtocol,
                                        SKCSectionActionProtocol,
                                        SKSafeSizeProviderProtocol {
    enum CellType {
        case header(HeaderCell.Model)
        case item(ItemCell.Model)
        case footer(FooterCell.Model)
    }

    var sectionInjection: SKCSectionInjection?
    lazy var safeSizeProvider: SKSafeSizeProvider = defaultSafeSizeProvider
    var cellTypes: [CellType] = []

    var itemCount: Int { cellTypes.count }

    init(cellTypes: [CellType] = []) {
        self.cellTypes = cellTypes
    }

    func config(sectionView: UICollectionView) {
        register(HeaderCell.self)
        register(ItemCell.self)
        register(FooterCell.self)
    }

    func itemSize(at row: Int) -> CGSize {
        switch cellTypes[row] {
        case .header(let model):
            return HeaderCell.preferredSize(limit: safeSizeProvider.size, model: model)
        case .item(let model):
            return ItemCell.preferredSize(limit: safeSizeProvider.size, model: model)
        case .footer(let model):
            return FooterCell.preferredSize(limit: safeSizeProvider.size, model: model)
        }
    }

    func item(at row: Int) -> UICollectionViewCell {
        switch cellTypes[row] {
        case .header(let model):
            let cell = dequeue(at: row) as HeaderCell
            cell.config(model)
            return cell
        case .item(let model):
            let cell = dequeue(at: row) as ItemCell
            cell.config(model)
            return cell
        case .footer(let model):
            let cell = dequeue(at: row) as FooterCell
            cell.config(model)
            return cell
        }
    }

    func item(selected row: Int) {
        if case .item(let model) = cellTypes[row] {
            print("Item tapped: \(model.title)")
        }
    }
}

```

## Usage Example

Instantiate the section with an array of `CellType` values and pass it to your `SKCManager`.

```swift
let mixedSection = MixedCellsSectionTemplate(cellTypes: [
    .header(.init(title: "Profile")),
    .item(.init(title: "Name", subtitle: "Lin Hay")),
    .item(.init(title: "Email", subtitle: "lin@example.com")),
    .footer(.init(text: "2 items"))
])

manager.reload(mixedSection)

```

The manager handles the rest, calling the appropriate sizing and cell provision methods as the collection view scrolls.

## Summary

- **Conform to `SKCSectionProtocol`** to satisfy the manager’s data source and delegate requirements.
- **Adopt `SKCSectionActionProtocol`** to gain access to `register(_:)` and `dequeue(at:)` helper methods.
- **Model heterogeneous data with an enum** where each case contains the model for a specific cell type.
- **Switch on the enum** inside `itemSize(at:)` and `item(at:)` to branch logic for each cell class.
- **Register all cell classes** inside `config(sectionView:)` so the collection view can reuse cells correctly.

## Frequently Asked Questions

### How does SectionKit know which cell class to instantiate?

SectionKit relies on the `register(_:)` method called inside `config(sectionView:)` to map cell classes to reuse identifiers. When you call `dequeue(at:)` in `item(at:)`, the helper uses the identifier associated with the class you specify, ensuring the collection view returns the correct cell type.

### Can I use more than three cell types in one section?

Yes. The enum-based pattern scales to any number of cell types. Simply add new cases to your `CellType` enum, register the additional classes in `config(sectionView:)`, and add corresponding branches to your `switch` statements in `itemSize(at:)` and `item(at:)`.

### Do I need to handle cell reuse identifiers manually?

No. `SKCSectionActionProtocol` abstracts reuse identifiers away. The `register(_:)` and `dequeue(at:)` methods manage the mapping between Swift types and `UICollectionView` reuse identifiers automatically, reducing boilerplate and preventing identifier collisions.

### Where is the best place to handle cell selection logic?

Implement the `item(selected:)` method from `SKCSectionProtocol`. Inside this method, switch on the `CellType` enum at the provided index to execute type-specific behavior, such as navigation or state updates, without cluttering your view controller.