使用UITableViewDiffableDataSource删除section后headerView索引不更新如何解决
根本原因
使用UITableViewDiffableDataSource执行section删除操作时,仍停留在可视区域的headerView不会触发复用重绘,因此你存储在header中的section索引值还是删除操作前的旧值,只有header滚出屏幕再滚回触发复用逻辑时,才会更新为正确的索引。
最优解决方案(推荐)
不要在header中存储可变的section索引,直接存储唯一固定的SectionType枚举值,从根源避免索引过时问题:
- 修改Header类定义,替换原有存储的Int类型section属性
class MyTableHeaderView: UITableViewHeaderFooterView { // 删掉原有 var section: Int 定义 var sectionType: SectionType! // 替换为固定的枚举类型 weak var delegate: SomeTableHeaderViewDelegate? // 其余原有代码保持不变 @objc func viewAllButtonTapped() { delegate?.someTableHeaderViewTappedViewAll(self) } }
- 给DiffableDataSource补充header提供逻辑,每次header将要展示时直接赋值对应的SectionType
private func makeDataSource() -> UITableViewDiffableDataSource<SectionType, RowItemType> { let dataSource = UITableViewDiffableDataSource(tableView: tableView, cellProvider: { // 原有cellProvider的逻辑完全不变 }) // 新增头部视图提供逻辑 dataSource.supplementaryViewProvider = { [weak self] tableView, kind, indexPath in guard let self = self, kind == UITableView.elementKindSectionHeader else { return nil } let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "MyTableHeaderView") as! MyTableHeaderView let currentSection = self.dataSource.snapshot().sectionIdentifiers[indexPath.section] header.sectionType = currentSection // 直接存固定枚举,不依赖索引 header.delegate = self // 其余header配置逻辑(比如标题、样式等)写在这里 return header } return dataSource }
- 修改点击代理逻辑,直接使用header存储的SectionType判断,完全不依赖可变索引
extension SomeTableViewController: SomeTableHeaderViewDelegate { func someTableHeaderViewTappedViewAll(_ headerView: SomeTableHeaderView) { let snapshot = dataSource.snapshot() // 直接用header存储的枚举判断,无需通过索引获取 switch headerView.sectionType { case .sectionOne: if case .itemTypeOne(let collection) = snapshot.itemIdentifiers(inSection: .sectionOne).first, !collection.foos.isEmpty { let vc = SomeListViewController() navigationController?.pushViewController(vc, animated: true) } case .sectionTwo: if case .itemTypeTwo(let collection) = snapshot.itemIdentifiers(inSection: .sectionTwo).first, !collection.foos.isEmpty { let vc = SomeListViewController() navigationController?.pushViewController(vc, animated: true) } case .sectionThree: if case .itemTypeThree(let collection) = snapshot.itemIdentifiers(inSection: .sectionThree).first, !collection.bars.isEmpty { let vc = OtherViewController(collecton: collection) navigationController?.pushViewController(vc, animated: true) } } } }
临时补丁方案(不推荐)
如果不想修改原有header的存储逻辑,可以在每次应用快照后手动刷新所有可见header的索引:
在每次调用self.dataSource.apply(snapshot, animatingDifferences: true)之后补充以下代码:
DispatchQueue.main.async { for (currentIndex, header) in self.tableView.visibleSectionHeaderViews.enumerated() { guard let header = header as? MyTableHeaderView else { continue } header.section = currentIndex } }
内容的提问来源于stack exchange,提问作者Taylor Simpson
相关产品推荐
相关产品推荐

