使用UICollectionViewDiffableDataSource时更新数组后Section被重建的原因
我为UICollectionViewDiffableDataSource创建了如下Section结构体:
struct Section: Hashable { let name: String let items: [Int] }
并编写了更新数据的方法来生成快照:
func updateData(for data: [Section]) { var snapshot = NSDiffableDataSourceSnapshot<Section, Int>() snapshot.appendSections(data) for section in data { snapshot.appendItems(section.items, toSection: section.name) } dataSource.apply(snapshot, animatingDifferences: false) }
初始数据设置为:
updateData(for: [Section(name: "T", items: [1, 2]), Section(name: "J", items: [3, 4, 5])])
当调用该方法为Section "J"添加元素时:
updateData(for: [Section(name: "T", items: [1, 2]), Section(name: "J", items: [3, 4, 5 ,6])])
发现整个Section "J"被重新创建了,这是为什么?
UICollectionViewDiffableDataSource依靠Hashable协议的哈希值与相等性判断来识别是否为同一个Section。你的Section结构体使用了Swift自动合成的Hashable实现,会将所有属性(name和items)都纳入哈希计算与相等性比较。
当Section "J"的items数组新增元素后,新的Section实例与旧实例的items属性不同,导致两者哈希值不等、相等性判断返回false。DiffableDataSource会将其判定为全新的Section,因此会先删除旧的Section "J",再创建新的Section "J"。
另外,updateData方法中调用appendItems时传入section.name存在逻辑隐患:快照的Section类型是Section结构体,这里却传入了String类型的name,虽然Swift可能隐式处理,但这属于类型不匹配,会增加代码的不确定性。
要让DiffableDataSource正确识别同一Section,需让Section的哈希计算与相等性判断仅依赖唯一标识属性name,忽略items的变化。手动实现Hashable协议即可:
struct Section: Hashable { let name: String let items: [Int] static func == (lhs: Section, rhs: Section) -> Bool { lhs.name == rhs.name } func hash(into hasher: inout Hasher) { hasher.combine(name) } }
同时修正updateData方法中appendItems的参数,传入正确的Section实例:
func updateData(for data: [Section]) { var snapshot = NSDiffableDataSourceSnapshot<Section, Int>() snapshot.appendSections(data) for section in data { snapshot.appendItems(section.items, toSection: section) } dataSource.apply(snapshot, animatingDifferences: false) }
这样修改后,当Section "J"的items更新时,DiffableDataSource会识别出这是同一个Section,仅更新该Section内的元素,不会重建整个Section。
内容的提问来源于stack exchange,提问作者Mixorok

