iOS UICollectionView 差异化数据源与组合布局
iOS UICollectionView 差异化数据源与组合布局
摘要:复杂列表常包含横幅、横向卡片和纵向分组。UICollectionView 的 Diffable Data Source 与 Compositional Layout 可以分别管理数据变化和区域布局。本文介绍稳定标识、快照更新、布局构造与异步图片更新。
一、用稳定标识描述数据
Diffable Data Source 使用 Section 和 Item 标识,而不是依赖“第几行”。标识需要在多次刷新之间保持稳定:
enum Section: Hashable {
case featured
case recommendations
}
struct Card: Hashable {
let id: UUID
var title: String
var imageKey: String
}
若用可变标题作为 ID,改标题会被框架识别成删除旧项并插入新项,可能造成不必要动画和状态丢失。
二、创建 Diffable Data Source
注册 Cell 并将模型绑定到标识:
let registration = UICollectionView.CellRegistration<CardCell, Card> {
cell, indexPath, card in
cell.configure(title: card.title, imageKey: card.imageKey)
}
let dataSource = UICollectionViewDiffableDataSource<Section, Card.ID>(
collectionView: collectionView
) { collectionView, indexPath, itemID in
guard let card = store.card(for: itemID) else { return nil }
return collectionView.dequeueConfiguredReusableCell(
using: registration,
for: indexPath,
item: card
)
}
这里的数据存储需要能根据 ID 取得完整模型。快照只负责描述顺序与标识,模型本身可以由 Store 或 ViewModel 管理。
三、通过快照应用数据变化
每次用新快照表达当前列表结构:
func apply(_ cards: [Card], animated: Bool) {
var snapshot = NSDiffableDataSourceSnapshot<Section, Card.ID>()
snapshot.appendSections([.featured, .recommendations])
snapshot.appendItems(featuredIDs, toSection: .featured)
snapshot.appendItems(cards.map(\.id), toSection: .recommendations)
dataSource.apply(snapshot, animatingDifferences: animated)
}
数据源应用快照可能是异步过程。不要同时从多个线程对同一个数据源应用互相冲突的快照;把模型更新与快照提交串行化,并在必要时合并短时间内的变化。
四、按区域构造组合布局
Compositional Layout 让每个 Section 独立定义尺寸和滚动方向:
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .absolute(180)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.84),
heightDimension: .absolute(180)
)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .groupPaging
section.interGroupSpacing = 12
return section
}
实际布局可根据 sectionIndex 返回不同的 Section。environment 提供容器尺寸和尺寸类别等信息,适合在横竖屏或分屏宽度变化时调整卡片尺寸。
五、正确处理异步图片
Cell 会被复用,图片请求完成时原 Cell 可能已经显示另一张卡片。用模型 ID 校验结果,或让图片加载器按 ID 管理请求与缓存。Cell 重用时取消旧请求并重置占位图;图片完成后只更新仍对应同一模型的 Cell。
图片请求失败也要回到可接受的占位状态。不要在 Cell 配置方法中同步读取大文件或解码大图。
六、空状态、加载和增量更新
空结果和加载指示器可以作为独立 Section/Item,也可由背景视图表达。选择一种清楚的数据模型,避免空列表时留下旧 Cell。
若模型内容变化但 ID 不变,应显式告诉快照哪些项需要重新配置,或更新对应 Cell 内容。刷新整个集合能够工作,但大量数据频繁全量计算会浪费资源。优先确保模型存储先于 Cell 配置更新。
七、调试与性能
- 每个 Item ID 在同一快照中必须唯一。
- 快照中的 ID 应能解析到当前模型。
- 动画期间避免重复提交同一组冲突快照。
- 复杂布局需在不同宽度、字体大小和横竖屏下检查。
- 长列表使用尺寸估算,并避免 Cell 内重复创建昂贵资源。
八、总结
Diffable Data Source 用稳定 ID 描述列表变化,Compositional Layout 按 Section 描述不同布局。把模型存储、快照提交和 Cell 渲染分开,再妥善处理复用与异步图片,复杂集合视图会更容易扩展和维护。
- 点赞
- 收藏
- 关注作者
评论(0)