Modern cell configuration
Swift & UI 进阶 24m

现代 Cell 配置方式

Modern cell configuration

2020年6月26日

在 Apple 官方观看视频

一句话判断

Cell Configuration 是 iOS 14 在 UIKit 中引入的最重要的架构改进之一——它把 Cell 的配置逻辑从”在 cellForRowAt 中手动改 subview”变成了声明式的状态驱动方式,代码量和出错概率都大幅降低。

这场 Session 讲了什么

传统的 Cell 配置方式是在 cellForRow(at:) 中直接设置 Cell 的 subview 属性。这种方式的问题在于:你需要手动管理 Cell 的重置(否则复用 Cell 会显示上一条数据的状态),而且选中/高亮等状态变化需要在多个 delegate 方法中分别处理。

iOS 14 引入了 UIContentConfiguration 协议和 UIListContentConfiguration 标准实现。新的配置方式是声明式的:你创建一个 Configuration 对象描述 Cell 应该显示什么内容,系统自动处理状态变化和动画。

核心 API 包括:UIListContentConfiguration(标准列表 Cell 的配置,包含 text、secondaryText、image 等),UICollectionViewListCell.defaultContentConfiguration(),以及 cell.contentConfiguration 属性。Cell 还引入了 backgroundConfiguration 来独立配置背景。

值得深挖的点

从命令式到声明式的思维转变。 传统方式是”拿到 Cell → 修改 subview”,新方式是”创建 Configuration → 赋值给 Cell”。Configuration 是值类型(struct),这意味着你可以在 Cell 外部创建和测试配置,完全解耦了数据和 UI。

状态驱动的自动更新。 通过重写 updateConfiguration(using:) 方法,Cell 会在状态变化时(选中、高亮、拖拽等)自动调用这个方法。你只需要根据传入的 UICellConfigurationState 返回对应的 Configuration,系统会自动处理动画过渡。

代码片段

// 传统方式(不推荐)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    // 问题:复用 Cell 可能保留上一条数据的状态
    cell.textLabel?.text = items[indexPath.row].title
    cell.imageView?.image = items[indexPath.row].image
    return cell
}

// 现代方式:使用 Cell Configuration
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Item> { cell, indexPath, item in
    var content = cell.defaultContentConfiguration()
    content.text = item.title
    content.secondaryText = item.subtitle
    content.image = item.icon
    
    // 配置文本样式
    content.textProperties.font = .preferredFont(forTextStyle: .headline)
    content.textProperties.color = .label
    content.secondaryTextProperties.font = .preferredFont(forTextStyle: .caption1)
    
    // 配置图片
    content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
    content.imageProperties.cornerRadius = 8
    
    cell.contentConfiguration = content
}
// 状态驱动的 Configuration 更新
class CustomCell: UICollectionViewListCell {
    override func updateConfiguration(using state: UICellConfigurationState) {
        // 根据当前状态生成新的 Configuration
        var content = defaultContentConfiguration().updated(for: state)
        
        if state.isSelected {
            content.textProperties.color = .systemBlue
            content.imageProperties.tintColor = .systemBlue
        } else {
            content.textProperties.color = .label
            content.imageProperties.tintColor = .secondaryLabel
        }
        
        if state.isHighlighted {
            content.textProperties.font = .preferredFont(forTextStyle: .headline)
        }
        
        contentConfiguration = content
        
        // 同时更新背景配置
        var background = UIBackgroundConfiguration.listGroupedCell().updated(for: state)
        if state.isSelected {
            background.backgroundColor = .systemBlue.withAlphaComponent(0.1)
        }
        self.backgroundConfiguration = background
    }
}
// Configuration 是值类型,可以在外部创建和复用
struct CellConfigurator {
    static func makeContentConfiguration(for item: Item) -> UIListContentConfiguration {
        var content = UIListContentConfiguration.valueCell()
        content.text = item.title
        content.secondaryText = item.detail
        content.image = UIImage(systemName: item.iconName)
        
        // 根据内容类型调整样式
        if item.isImportant {
            content.textProperties.font = .preferredFont(forTextStyle: .headline)
            content.imageProperties.tintColor = .systemRed
        }
        
        return content
    }
}

最佳实践

  • 重写 updateConfiguration(using:) 而非在 delegate 中处理状态。 让 Cell 自己响应状态变化,代码更集中、更不容易遗漏。
  • 使用 .updated(for: state) 创建派生配置。 这个方法会在当前配置基础上应用状态相关的系统默认值,然后你再覆盖需要自定义的部分。
  • 将 Configuration 的创建逻辑封装到独立函数中。 Configuration 是值类型,非常适合提取到单独的配置器中做单元测试。
  • backgroundConfiguration 和 contentConfiguration 分开管理。 背景和内容是独立的关注点,分开配置更清晰。

还有什么值得关注

  • UIListContentConfiguration 提供了多种预设样式(valueCell、subtitleCell、sideBarCell 等)
  • backgroundConfiguration 支持自定义圆角、边框和阴影
  • 新的 Configuration API 同时支持 UICollectionView 和 UITableViewCell
  • 配合 Diffable Data Source 使用效果最佳
WWDC 2020