Update collection view supplementary view content

If you run the following UIKit app and tap the view controller's right bar button item, the footerText property will change.

How should I update the collection view's footer to display the updated footerText?

class ViewController: UIViewController {
    var collectionView: UICollectionView!
    
    var footerText = "Initial footer text"
    
    var dataSource: UICollectionViewDiffableDataSource<Section, String>!
    
    var snapshot: NSDiffableDataSourceSnapshot<Section, String> {
        var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
        snapshot.appendSections(Section.allCases)
        snapshot.appendItems(["A", "a"], toSection: .first)
        return snapshot
    }
    
    enum Section: CaseIterable {
        case first
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        configureHierarchy()
        configureDataSource()
    }
    
    func configureHierarchy() {
        navigationItem.rightBarButtonItem = .init(title: "Change footer text", style: .plain, target: self, action: #selector(changeFooterText))
        
        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createLayout())
        view.addSubview(collectionView)
        collectionView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
    }
    
    @objc func changeFooterText() {
        footerText = "Secondary footer text"
    }
    
    func configureDataSource() {
        let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> { cell, indexPath, itemIdentifier in
            var contentConfiguration = UIListContentConfiguration.cell()
            contentConfiguration.text = itemIdentifier
            cell.contentConfiguration = contentConfiguration
        }
        
        dataSource = .init(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
            collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: itemIdentifier)
        }
            
        configureSupplementaryViewProvider()
        
        dataSource.apply(self.snapshot)
    }
    
    func configureSupplementaryViewProvider() {
        let headerRegistration = UICollectionView.SupplementaryRegistration<UICollectionViewListCell>(elementKind: UICollectionView.elementKindSectionHeader) { headerView, elementKind, indexPath in
            
            var contentConfiguration = UIListContentConfiguration.cell()
            contentConfiguration.text = "Header \(indexPath.section)"
            headerView.contentConfiguration = contentConfiguration
        }
        let footerRegistration = UICollectionView.SupplementaryRegistration<UICollectionViewListCell>(elementKind: UICollectionView.elementKindSectionFooter) { [weak self] headerView, elementKind, indexPath in
            
            guard let self else { return }
            
            var contentConfiguration = UIListContentConfiguration.cell()
            contentConfiguration.text = self.footerText
            headerView.contentConfiguration = contentConfiguration
        }
        
        dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in
            if kind == UICollectionView.elementKindSectionHeader {
                collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration, for: indexPath)
            } else if kind == UICollectionView.elementKindSectionFooter {
                collectionView.dequeueConfiguredReusableSupplementary(using: footerRegistration, for: indexPath)
            } else {
                nil
            }
        }
    }
    
    func createLayout() -> UICollectionViewLayout {
        UICollectionViewCompositionalLayout { section, layoutEnvironment in
            var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
            config.headerMode = .supplementary
            config.footerMode = .supplementary
            return NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment)
        }
    }
}

What I've tried to do in footerText's didSet:

  1. Reconfiguring the supplementary view provider:
var footerText = "Initial footer text" {
    didSet {
        configureSupplementaryViewProvider()
    }
}
  1. Also re-applying the snapshot:
var footerText = "Initial footer text" {
    didSet {
        configureSupplementaryViewProvider()
        dataSource.apply(self.snapshot)
    }
}
  1. Also re-configuring the items:
var footerText = "Initial footer text" {
    didSet {
        configureSupplementaryViewProvider()
        dataSource.apply(self.snapshot, animatingDifferences: true)

        var snapshot = dataSource.snapshot()
        snapshot.reconfigureItems(snapshot.itemIdentifiers)
        dataSource.apply(snapshot, animatingDifferences: false)
    }
}

Accepted Reply

https://stackoverflow.com/questions/78311570/how-do-i-update-a-collection-view-supplementary-view-without-giving-up-on-animat

At this time the answer is:

To directly update a supplementaryView you need to know its kind and section. With your example, you could add a following didSet code to a footerText:

var footerText = "Initial footer text" {
    didSet {
        guard let footer = collectionView.supplementaryView(forElementKind: UICollectionView.elementKindSectionFooter, at: IndexPath(row: 0, section: 0)) as? UICollectionViewListCell else { return }
        
        var contentConfiguration = UIListContentConfiguration.cell()
        contentConfiguration.text = self.footerText
        footer.contentConfiguration = contentConfiguration
    }
}

This way each time you change footerText, your footer will be updated. You could also move contentConfiguration update to a separate fuction, I've just copied it from your configureSupplementaryViewProvider

Replies

https://stackoverflow.com/questions/78311570/how-do-i-update-a-collection-view-supplementary-view-without-giving-up-on-animat

At this time the answer is:

To directly update a supplementaryView you need to know its kind and section. With your example, you could add a following didSet code to a footerText:

var footerText = "Initial footer text" {
    didSet {
        guard let footer = collectionView.supplementaryView(forElementKind: UICollectionView.elementKindSectionFooter, at: IndexPath(row: 0, section: 0)) as? UICollectionViewListCell else { return }
        
        var contentConfiguration = UIListContentConfiguration.cell()
        contentConfiguration.text = self.footerText
        footer.contentConfiguration = contentConfiguration
    }
}

This way each time you change footerText, your footer will be updated. You could also move contentConfiguration update to a separate fuction, I've just copied it from your configureSupplementaryViewProvider