NSDiffableSnapshot causes collectionview's cell registration to be re-called even without any changes to the data

空扰寡人 提交于 2021-01-29 13:38:41

问题


I have a problem with NSDiffableDataSource. I have created this basic example containing a UICollectionView with a single item showing a random number:

import UIKit

class ViewController: UIViewController {

    enum Section {
        case main
    }
    
    enum Item {
        case myItem
    }
    
    var collectionView: UICollectionView!
    var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
    
    lazy var myCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Void> { (cell, indexPath, _) in
        print("Cell registration called")
        var content = cell.defaultContentConfiguration()
        content.text = "\(Int.random(in: 0...10000))"
        cell.contentConfiguration = content
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
        
        let layout = UICollectionViewCompositionalLayout.list(using: config)
        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.backgroundColor = .systemGroupedBackground
        
        view.addSubview(collectionView)
        
        NSLayoutConstraint.activate([
            collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            collectionView.topAnchor.constraint(equalTo: view.topAnchor),
            collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        
        dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView, cellProvider: { [unowned self] (collectionView, indexPath, item) -> UICollectionViewCell? in
            
            switch item {
            case .myItem:
                return collectionView.dequeueConfiguredReusableCell(using: myCellRegistration, for: indexPath, item: ())
            }
        })
        
        DispatchQueue.global(qos: .background).async {
            while true {
                sleep(2)
                DispatchQueue.main.async {
                    var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
                    snapshot.appendSections([.main])
                    snapshot.appendItems([.myItem])
                    self.dataSource.apply(snapshot, animatingDifferences: true)
                }
            }
        }
        
    }
}

Every 2 seconds, I re-apply the same snapshot to the data source. As the snapshot doesn't change, I expect the cell to not be updated either. And that is the case ... kind of. Visually, the cell does not change (the displayed number stays the same). However, the data source actually calls the cell registration closure every time the snapshot updates, so the cell's content configuration actually does change. It's just not being drawn.

Does anybody know what's going on here?

To give a little bit of background: The reason I'm asking about this is that I want to subscribe to a Combine publisher when creating the content configuration of the cell. The publisher then publishes the data for the cell and the cell can update its views, allowing custom animations. (I could simply put the data into an item identifier, but then any data change would simply result in the cell cross-fading into the new one. That's not nice).

It would look something like this:

// Event.Cell is some custom cell with a custom content configuration
lazy var myCellRegistration = UICollectionView.CellRegistration<Event.Cell, Void> { [unowned self] (cell, indexPath, _) in
    var content = Event.ContentConfiguration()
    
    self.myEventPublisher.sink { event in
        content.event = event
        cell.contentConfiguration = content // Re-set the contentConfiguration
    }.store(in: &content.disposeBag) // store the subscriber in a dispose bag inside the content configuration
}

Since working with publishers and subscribers can be tricky (memory leaks and stuff), I need to know how to do this without causing any problems and the snapshot is behaving strangely. At least to me it seems that way

来源:https://stackoverflow.com/questions/64818655/nsdiffablesnapshot-causes-collectionviews-cell-registration-to-be-re-called-eve

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