You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UICollectionView Diffable Data Source如何保留跨区移动动画并更新header内容

问题原因

Diffable Data Source 依赖 Section 标识符的 Hashable/Equatable 协议实现判断两个 section 是否为同一个。你给 checked case 增加了关联数量后,Swift 自动合成的协议实现会把关联的 Int 值纳入判断,导致数量变化前后的 .checked(旧数量) 和 .checked(新数量) 被识别为两个完全不同的 section。Diffable 会直接删除旧 section 插入新 section,自然无法触发跨 section 的 item 移动动画。


解决方案

推荐两种可行方案,优先推荐第一种更符合 Diffable 设计规范的实现:

方案1:展示数据与 Section 标识符分离(推荐)

核心原则:Diffable 的 Section/Item 标识符只负责唯一标识,不要存放视图展示用的动态数据。

  1. 把 Section 定义改回无关联值的版本:
enum Section: Hashable {
    case unchecked
    case checked
}
  1. 恢复原来的 applySnapshot 逻辑,不需要给 Section 传数量。
  2. header 展示时直接读取当前已勾选列表的数量:
dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in
    guard kind == UICollectionView.elementKindSectionHeader,
          let checklistHeader = collectionView.dequeueReusableSupplementaryView(
              ofKind: kind,
              withReuseIdentifier: "header",
              for: indexPath
          ) as? ChecklistHeader else {
        return nil
    }
    let section = self.dataSource.snapshot().sectionIdentifiers[indexPath.section]
    switch section {
    case .unchecked:
        checklistHeader.label.text = "Header with unchecked item"
    case .checked:
        // 直接读当前已勾选数组的数量,不需要存在Section里
        checklistHeader.label.text = "Header with checked item \(self.checkedChecklists.count)"
    }
    checklistHeader.delegate = self
    return checklistHeader
}

这种方案不需要修改协议实现,逻辑更简洁,后续维护成本更低。


方案2:自定义 Section 的 Hashable/Equatable 实现

如果一定要把数量存在 Section 的关联值里,可以手动重写协议方法,让关联的数量不参与相等判断和哈希计算:

enum Section: Hashable {
    case unchecked
    case checked(Int)
    
    static func == (lhs: Self, rhs: Self) -> Bool {
        switch (lhs, rhs) {
        case (.unchecked, .unchecked): return true
        case (.checked, .checked): return true
        default: return false
        }
    }
    
    func hash(into hasher: inout Hasher) {
        switch self {
        case .unchecked: hasher.combine(0)
        case .checked: hasher.combine(1)
        }
    }
}

修改后无论 .checked 携带的数量是多少,都会被识别为同一个 section,既可以保留移动动画,也可以通过关联值传递数量给 header 展示。


内容的提问来源于stack exchange,提问作者Cheok Yan Cheng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 08:48:02