Diffable Data Source - Reload Header

2.7k Views Asked by At

What are the ways in which a UICollectionViewDiffableDataSource header can be reloaded?

I have a collection view with a header that displays user details, and rows that display posts, the model is

struct PostUser {
    var user: User
    var post: Post
}

when I change a property via the snapshot

    var postUsers = [PostUser]() {
        didSet {
            self.applySnapshot(postUsers)
        }
    }

    fileprivate func applySnapshot(_ postsUser: [PostUser]) {
        var snapshot = NSDiffableDataSourceSnapshot<Section, PostUser>()
        snapshot.appendSections([.main])
        snapshot.appendItems(postsUser)
        self.datasource.apply(snapshot, animatingDifferences: true)
    }

the rows reload, however the supplementary header does not. The only way I can get the header to change is if I make the Section part of the model, so:

    struct Section: Hashable {
        var User: User
    }

my apply snapshot now becomes

    fileprivate func applySnapshot(_ postsUser: [PostUser]) {
        var snapshot = NSDiffableDataSourceSnapshot<Section, PostUser>()
        snapshot.appendSections([Section(User: self.user)])
        snapshot.appendItems(postsUser)
        self.datasource.apply(snapshot, animatingDifferences: true)
    }

I then set the user separately

    var user: User! = nil {
        didSet {
            self.applySnapshot(self.postUsers)
        }
    }

and the header reloads.

I don't quite understand why when I change something in postUsers, the rows reload, but the header does not - until I implement a model as part of the section?

I do understand that diffable works on hashing, and so when I change a property, the table reloads, but it feels like the header should also reload, but it is treated separately?

2

There are 2 best solutions below

2
On

Headers are only reloaded when a change in the section itself is detected. If only the items change, the supplementary views will remain unchanged. Another thing you could look into depending on your case would be to set animatingDifferences to false when you want to reload a header since, as of the latest iOS 14 beta, that will trigger reloadData. No diffing will be done.

0
On

I figure out to use reloadSections for iOS 15 previous is workable.

private func applyNewSnapshot() {
    var newSnapshot = self.dataSource.snapshot()
    if #available(iOS 15.0, *) {
        self.dataSource.applySnapshotUsingReloadData(newSnapshot)
    } else {
        // Fallback on earlier versions
        newSnapshot.reloadSections([.main])
        self.dataSource.apply(newSnapshot, animatingDifferences: false)
    }
}