DiffableDataSource:快照不会重新加载页眉和页脚

FE_*_*ech 8 ios uicollectionview uicollectionviewlayout swift diffabledatasource

我正在使用UICollectionViewDiffableDataSourceforUICollectionView在多个部分中显示内容。

我正在使用在 WWDC'19 上引入的Collection View Compositional Layout 和 Diffable Datasources链接来呈现的多节布局UICollectionView

我有一个简单的设置,每个部分的页眉显示该部分中的项目数,页脚显示该部分所有项目的摘要。

section 1 Header --> January 2020 - 5 Trips
section 1 item 1 --> Trip 1
section 1 item 2 --> Trip 2
section 1 item 3 --> Trip 3
section 1 item 4 --> Trip 4
section 1 item 5 --> 行程 5

现在如果行程被删除,DiffableDataSource 会通过动画更新更改,但不会重新加载各部分的标题。这看起来不一致。例如,如果行程 4 被删除,则标题仍显示该部分中有 5 个行程。我怎样才能用 DiffableDataSource 重新加载标头?

对于临时修复,我只是collectionView.reloadData()在显示 Diffing 动画的延迟后调用 ,然后我硬重新加载数据,这也强制重新加载标头。

private func configureTripDataSource(){
    tripDataSource = UICollectionViewDiffableDataSource<MonthSection, Trip>(collectionView: tripsCollectionView, cellProvider: { (collectionView, indexPath, trip) -> UICollectionViewCell? in

        // Get a cell of the desired kind.
        guard let cell = collectionView.dequeueReusableCell(
            withReuseIdentifier: TripInfoCell.reuseIdentifier,
            for: indexPath) as? TripInfoCell else { fatalError("Cannot create new TripInfoCell") }

        // Populate the cell with our item description.
        cell.trip = trip

        // Return the cell.
        return cell

    })

    tripDataSource.supplementaryViewProvider = {
       [weak self] (collectionView: UICollectionView, kind: String, indexPath: IndexPath) -> UICollectionReusableView? in

        guard let self = self else {return nil}

        if kind == TripsController.tripsMonthSectionHeaderElementKind{

            // Get a supplementary view of the desired kind.
            guard let header = collectionView.dequeueReusableSupplementaryView(
                ofKind: kind,
                withReuseIdentifier: TripSectionHeaderCell.reuseIdentifier,
                for: indexPath) as? TripSectionHeaderCell else { fatalError("Cannot create new header") }

            // setup header

            let currentSnapShot = self.tripDataSource.snapshot()
            let tripMonthSection = currentSnapShot.sectionIdentifiers[indexPath.section]

            header.titleLabel.text = tripMonthSection.title
            header.subtitleLabel.text = "\(tripMonthSection.trips.count) Trips"

            return header

        } else {
            return UICollectionReusableView()
        }

    }

    var snapshot = NSDiffableDataSourceSnapshot<MonthSection, Trip>()

    let allSections = self.tripsStore.monthSections
    snapshot.appendSections(allSections)
    for section in allSections{
        snapshot.appendItems(section.trips, toSection: section)
    }

    self.tripDataSource.apply(snapshot, animatingDifferences: true)
}
Run Code Online (Sandbox Code Playgroud)

小智 9

要触发标题的自动重新加载,您的Section对象应该是Hashable并且应该存储所有必要的属性以创建该 Section 的唯一哈希。

这就是为什么所有Section对象和Item对象都应该是Hashable并且它们应该返回一个唯一的哈希值,以允许DiffableDataSource仅在它们的值发生更改时管理它们的重新加载。

例如:

struct MonthSection: Hashable {
   var title: String
   var itemsCount: Int
}
Run Code Online (Sandbox Code Playgroud)

进而:

var section = MonthSection(title: "Title", itemsCount: 5)
.....
snapshot.appendSections([section])
snapshot.appendItems(items, toSection: section)
Run Code Online (Sandbox Code Playgroud)

在快照的下一次更新期间对该部分的标题或项目计数的任何更改都将触发部分标题重新加载,并且它会像魔术一样工作!