WWDC Quick Look 💓 By SwiftGGTeam
Lists in UICollectionView

Lists in UICollectionView

观看原视频

Highlight

iOS 14 让 UICollectionView 通过 UICollectionLayoutListConfiguration 创建 UITableView 风格列表,并用 UICollectionViewListCell 配置自适应高度、分隔线、滑动操作和 accessories。


核心内容

很多 UIKit 项目都有一个尴尬的分界线:简单列表用 UITableView,复杂布局用 UICollectionView。一旦页面同时需要列表、横向滚动区域、网格或层级结构,开发者就要在两个体系之间切换,数据源和布局代码也会被拆开。

iOS 14 把列表放进了 UICollectionView。UICollectionLayoutListConfiguration 提供 plain、grouped、inset-grouped,以及面向 iPad sidebar 的 sidebar、sidebar plain 外观。它建立在 Compositional Layout 上,所以同一个 collection view 里可以有表格风格列表、正交滚动区域和自定义网格。

列表 cell 也被重新设计。UICollectionViewListCell 支持默认自适应高度、分隔线布局 guide、leading/trailing swipe actions,以及可声明的 accessories。演讲中特别提醒:swipe action 不要捕获 index path,因为插入或删除上方内容后,旧 index path 可能指向另一条数据。

这让 UICollectionView 成为列表页面的默认起点。你可以先用它替换原来的 table view 外观,之后再按 section 加入横向区域、outline 或 iPad sidebar。


详细内容

创建整个 collection view 的列表布局

03:47)最简单的入口是创建 UICollectionLayoutListConfiguration,再交给 UICollectionViewCompositionalLayout.list(using:)。这个方式适合所有 section 都使用同一种列表外观的页面。

// Simple setup

let configuration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
let layout = UICollectionViewCompositionalLayout.list(using: configuration)

关键点:

  • UICollectionLayoutListConfiguration 是布局侧创建列表所需的新类型。
  • appearance: .insetGrouped 会生成类似 inset grouped table view 的外观。
  • UICollectionViewCompositionalLayout.list(using:) 直接生成整个 collection view 的布局。

按 section 混合列表和自定义布局

04:40)如果页面里只有部分 section 是列表,就用 Compositional Layout 的 section provider。每次系统请求一个 section 的布局时,你都可以返回列表 section 或自定义 section。

// Per section setup

let layout = UICollectionViewCompositionalLayout() {
    [weak self] sectionIndex, layoutEnvironment in
    guard let self = self else { return nil }

    // @todo: add custom layout sections for various sections

    let configuration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
    let section = NSCollectionLayoutSection.list(using: configuration, layoutEnvironment: layoutEnvironment)
    return section
}

关键点:

  • section provider 接收 sectionIndexlayoutEnvironment,可以按 section 返回不同布局。
  • NSCollectionLayoutSection.list(using:layoutEnvironment:) 只创建当前 section 的列表布局。
  • 自定义网格、横向滚动区域和列表可以共存于同一个 UICollectionViewCompositionalLayout

05:49)列表里的 header 和 footer 需要显式开启。把 headerModefooterMode 设为 .supplementary 后,collection view 会向 data source 或 delegate 请求 supplementary view。

var configuration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
configuration.headerMode = .supplementary
let layout = UICollectionViewCompositionalLayout.list(using: configuration)

dataSource.supplementaryViewProvider = { (collectionView, elementKind, indexPath) in
    if elementKind == UICollectionView.elementKindSectionHeader {
        return collectionView.dequeueConfiguredReusableSupplementary(using: header, for: indexPath)
    }
    else {
        return nil
    }
}

关键点:

  • .supplementary 表示 header 由 supplementary view 提供。
  • supplementaryViewProvider 需要判断 UICollectionView.elementKindSectionHeader 或 footer 对应的 element kind。
  • transcript 明确提醒:开启 supplementary 模式后,请求到 header/footer 时不能返回 nil,否则 collection view 会触发断言。

07:07)层级数据更适合 firstItemInSection。它把 section 的第一个 item 配置成 header 外观,适合与 section snapshot 搭配。

var configuration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
configuration.headerMode = .firstItemInSection
let layout = UICollectionViewCompositionalLayout.list(using: configuration)

关键点:

  • firstItemInSection 只适用于 header。
  • 使用这个模式时,data source 里的第一个 item 代表 header,而非普通内容。
  • 演讲建议把它用于层级数据结构和新的 section snapshot API。

在 cell 上配置 swipe actions

11:40)在 list cell 里,swipe actions 是 cell 的能力。你在配置内容的同一个位置设置 leadingSwipeActionsConfiguration 或 trailing 方向的配置。

let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Model> { (cell, indexPath, item) in
    // @todo configure the cell's content

    let markFavorite = UIContextualAction(style: .normal, title: "Mark as Favorite") {
        [weak self] (_, _, completion) in
        guard let self = self else { return }
        // trigger the action with a reference to the model
        self.markItemAsFavorite(with: item.identifier)
        completion(true)
    }
    cell.leadingSwipeActionsConfiguration = UISwipeActionsConfiguration(actions: [markFavorite])
}

关键点:

  • swipe action 只有在 cell 渲染于 list configuration 创建的 section 中才受支持。
  • action handler 捕获的是 item.identifier,不是 indexPath
  • 这个写法适合与 diffable data source 的稳定 item identifier 以及 iOS 14 的 cell registration 配合。

用 accessories 声明 cell 功能

14:55UICollectionViewListCell 的 accessories 可以配置在 leading 或 trailing 侧,同一侧也能放多个 accessory。部分 accessory 还会触发行为,例如 delete accessory 会显示 trailing swipe actions。

let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> { (cell, indexPath, item) in
    // @todo configure the cell's content

    cell.accessories = [
        .disclosureIndicator(),
        .delete()
    ]
}

关键点:

  • cell.accessories 接收一组 UICellAccessory
  • 系统知道 disclosure indicator 属于 trailing,delete accessory 属于 leading。
  • delete accessory 默认只在 editing mode 显示,进入和退出编辑模式时由 UIKit 处理动画。

15:51)accessory 的默认显示规则可以通过参数调整。下面的 disclosure indicator 只在非编辑模式显示。

let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> { (cell, indexPath, item) in
    // @todo configure the cell's content

    cell.accessories = [
        .disclosureIndicator(displayed: .whenNotEditing),
        .delete()
    ]
}

关键点:

  • displayed: .whenNotEditing 改变 accessory 的显示状态。
  • 这类声明式配置让 cell 在普通态和编辑态之间自动切换。
  • 复杂列表可以把导航、删除、重排和 outline 展开放在同一个 cell accessory 系统里处理。

核心启发

  • 把旧 table view 列表迁移到 UICollectionView:做一个外观保持不变的设置页或收藏页。列表 configuration 可以复用 plain、grouped、inset-grouped 风格,后续再逐步加入横向推荐区或网格 section。
  • 做一个 iPad sidebar + 内容区的资料管理界面:用 sidebar 或 sidebar plain 外观承载分组导航,适配 transcript 中提到的 iPadOS multi-column app 场景。起步时先让导航列使用 list configuration,再按 section 增加横向最近项目或层级分组。
  • 给列表项加入稳定的滑动操作:为邮件、笔记、收藏夹实现“标记为收藏”或“归档”。实现时在 UICollectionView.CellRegistration 中配置 UIContextualAction,handler 捕获 model 或稳定 identifier。
  • 用 accessories 替换手写编辑控件:为文件夹、任务列表或大纲页面添加 disclosure、delete、reorder 等 accessory。先设置 cell.accessories,再接入对应的数据源回调。
  • 按 section 混合信息密度:在一个页面顶部放横向最近项目,中间放 outline,底部放普通列表。section provider 可以按 section 返回网格、正交滚动布局或 NSCollectionLayoutSection.list(using:layoutEnvironment:)

关联 Session

  • Advances in UICollectionView — 从更高层介绍 iOS 14 的 collection view 更新,包括 lists、cell registration 和 section snapshots。
  • Advances in diffable data sources — 深入讲 section snapshots、outline collection views、sidebar 和 item-based reordering。
  • Modern cell configuration — 补充 cell content/background configuration,与 list cell 的默认内容和状态更新配合使用。
  • Build for iPad — 讲 iPad 多栏布局、sidebar 和列表在大屏界面中的使用方式。

评论

GitHub Issues · utterances