Custom Cells

Create custom cell layouts using Auto Layout for complete visual control.

When to Use Custom Cells

While SwiftDataTables' default cells handle most cases, you may need custom layouts for:

  • Complex multi-element cells
  • Images or icons alongside text
  • Interactive elements (buttons, switches)

Just need custom fonts or colours? Use defaultCellConfiguration instead - it's much simpler.

Step 1: Create Your Cell Class

class ProductCell: UICollectionViewCell {
    let nameLabel = UILabel()
    let priceLabel = UILabel()
    let statusIndicator = UIView()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupViews() {
        contentView.addSubview(nameLabel)
        contentView.addSubview(priceLabel)
        contentView.addSubview(statusIndicator)

        nameLabel.font = .systemFont(ofSize: 14, weight: .medium)
        priceLabel.font = .systemFont(ofSize: 14)
        priceLabel.textColor = .secondaryLabel

        // Auto Layout constraints...
    }

    func configure(name: String, price: String, isActive: Bool) {
        nameLabel.text = name
        priceLabel.text = price
        statusIndicator.backgroundColor = isActive ? .systemGreen : .systemRed
    }
}

Step 2: Create a Custom Cell Provider

let provider = DataTableCustomCellProvider(
    register: { collectionView in
        collectionView.register(ProductCell.self, forCellWithReuseIdentifier: "product")
    },
    reuseIdentifierFor: { indexPath in
        return "product"
    },
    configure: { cell, value, indexPath in
        guard let productCell = cell as? ProductCell else { return }
        let text = value.stringRepresentation
        productCell.configure(name: text, price: "$99", isActive: true)
    },
    sizingCellFor: { reuseIdentifier in
        return ProductCell()  // Off-screen cell for measurement
    }
)

Step 3: Configure the Table

var config = DataTableConfiguration()
config.cellSizingMode = .autoLayout(provider: provider)
config.rowHeightMode = .automatic(estimated: 60)

let dataTable = SwiftDataTable(data: items, columns: columns, options: config)

Different Cells for Different Columns

let provider = DataTableCustomCellProvider(
    register: { collectionView in
        collectionView.register(TextCell.self, forCellWithReuseIdentifier: "text")
        collectionView.register(ImageCell.self, forCellWithReuseIdentifier: "image")
        collectionView.register(ActionCell.self, forCellWithReuseIdentifier: "action")
    },
    reuseIdentifierFor: { indexPath in
        switch indexPath.section {  // Column index
        case 0: return "image"
        case 4: return "action"
        default: return "text"
        }
    },
    // ...
)

The sizingCellFor closure is called once per reuse identifier. The returned cell is cached and reused for measurements.