DiffableDataSource: Snapshot Doesn't reload Headers & footers

喜夏-厌秋 提交于 2020-12-30 07:52:24

问题


I am using UICollectionViewDiffableDataSource for UICollectionView to display content in multiple sections.

I am using Collection View Compositional Layout and Diffable Datasources link which was introduced at WWDC'19 to render the Multiple Section Layout of UICollectionView

I have a simple setup, The Header for each section shows number of items in that section, and Footer shows the summary of all items of the section.

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 --> Trip 5

now If a trip is deleted, the DiffableDataSource updates the change by animation but it doesn't reload the Headers of the sections. Which looks inconsistent. E.g. If the Trip 4 was deleted then Header still shows that there are 5 trips in the section. How can I have headers also reload with the DiffableDataSource?

for a temporary fix, I just call collectionView.reloadData() after a delay which shows the Diffing animation and then I hard reload the data which forces the header to be reloaded as well.

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)
}

回答1:


To trigger an automatic reload of headers your Section object should be Hashable and should have all the necessary properties stored to create a unique hash for the Section.

That's why all Section objects and Item objects should be Hashable and they should return a unique hash to allow DiffableDataSource to manage their reload only if their values were changed.

For example:

struct MonthSection: Hashable {
   var title: String
   var itemsCount: Int
}

And then:

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

Any change of the title or items count for the section during the next update of the snapshot will trigger section header to reload and it will work like magic!




回答2:


you must use the reloadSections of the snapshot to trigger the update, when appropriate

func buildSnapshotAndApply(animated: Bool = true) {
    
    var newSnapshot = buildSnapshot()
    newSnapshot.reloadSections(snapshot().sectionIdentifiers)
    
    apply(newSnapshot, animatingDifferences: animated)
}


来源:https://stackoverflow.com/questions/59980398/diffabledatasource-snapshot-doesnt-reload-headers-footers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!