问题
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