Diffable Data Source with a custom collection view layout?

Here I have created a sample app that uses diffable data source for a collection view with a custom collection view layout. The specific layout I am using is from this tutorial.

Here is the relevant part of the code if you don't want to clone the repo and try it for yourself.

import UIKit
let cellIdentifier = "testRecordCell"

struct Record:Hashable {
    let identifier = UUID()
    func hash(into hasher: inout Hasher) {
        hasher.combine(identifier)
    }
    static func == (lhs: Record, rhs: Record) -> Bool {
        return lhs.identifier == rhs.identifier
    }
    var timeStamp: Date
    init(daysBack: Int){
        self.timeStamp = Calendar.current.date(byAdding: .day, value: -1*daysBack, to: Date())!
    }
}
class Cell:UICollectionViewCell {
}

class Section: Hashable {
  var id = UUID()
  // 2
  var records:[Record]
  
  init(records:[Record]) {
    self.records = records
  }
  
  func hash(into hasher: inout Hasher) {
    hasher.combine(id)
  }
  
  static func == (lhs: Section, rhs: Section) -> Bool {
    lhs.id == rhs.id
  }
}

extension Section {
  static var allSections: [Section] = [
    Section(records: [
      Record(daysBack: 5),Record(daysBack: 6)
    ]),
    Section(records: [
      Record(daysBack: 3)
    ])
    ]
}

class ViewController: UICollectionViewController {

    private lazy var dataSource = makeDataSource()
    private var sections = Section.allSections

    fileprivate typealias DataSource = UICollectionViewDiffableDataSource<Section,Record>
    fileprivate typealias DataSourceSnapshot = NSDiffableDataSourceSnapshot<Section,Record>
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.collectionView?.register(Cell.self, forCellWithReuseIdentifier: cellIdentifier)
        applySnapshot()
        if let layout = collectionView.collectionViewLayout as? PinterestLayout {
            layout.delegate = self
        }
    }
}
extension ViewController {
    
    fileprivate func makeDataSource() -> DataSource {
        let dataSource = DataSource(
            collectionView: self.collectionView,
            cellProvider: { (collectionView, indexPath, testRecord) ->
              UICollectionViewCell? in
                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath)
                cell.backgroundColor = .black
                return cell
            })
        return dataSource
    }
    func applySnapshot(animatingDifferences: Bool = true) {
        // 2
        var snapshot = DataSourceSnapshot()
        snapshot.appendSections(sections)
        sections.forEach { section in
          snapshot.appendItems(section.records, toSection: section)
        }
        //This part errors out: "request for number of items in section 0 when there are only 0 sections in the collection view"
        dataSource.apply(snapshot, animatingDifferences: animatingDifferences)
    }
}
extension ViewController: PinterestLayoutDelegate {
    func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath: IndexPath) -> CGFloat {
        return CGFloat(10)
    }
}

Somehow, the layout is not registering that the collection view does have items and sections in it. When you run it normally, it errors out when you are trying to apply the snapshot: "request for number of items in section 0 when there are only 0 sections in the collection view"

Then, in the prepare() function of the Pinterest layout, when I set a breakpoint and inspect collectionView.numberOfSections() it returns 0. So somehow the snapshot is not communicating with the collection view. Notice that I never use the collectionView's delegate method numberOfSections because I am using the diffable data source...

У меня сложилось впечатление, что диффузный источник данных обычно используется с композиционным макетом, хотя я нигде не видел, чтобы это было обязательным требованием.

Так есть ли способ сделать это?

2 ответа

Решение

Проблема в этой строке:

func applySnapshot(animatingDifferences: Bool = true) {

+ Изменить true к false и вы больше не рухнете.

Привет, уже слишком поздно для ответа, но я пробовал так же и с той же проблемой

В моем случае проблема заключается в подходе numberOfItems в collectionView

82 строки в PinterestLayout ниже "для элемента в 0..<collectionView.numberOfItems(inSection: 0) {"

Итак.. я ввожу фактическое количество элементов в снимок из представления в пользовательский макет

      myLayout.updateNumberOfItems(currentSnapshot.numberOfItems)
dataSource.apply(currentSnapshot)

и просто используйте numberOfItems вместо collectionView.numberOfItems.

и это работа для меня

Пожалуйста, дайте мне знать, если я ошибаюсь, спасибо ")