Я реализовал такую оболочку UICollectionView
struct CollectionView: UIViewControllerRepresentable {
// MARK: - Properties
let layout: UICollectionViewLayout
let sections: [Section]
let items: [Section: [Item]]
// MARK: - Actions
let content: (_ indexPath: IndexPath, _ item: Item) -> AnyView
init(layout: UICollectionViewLayout,
sections: [Section],
items: [Section: [Item]],
@ViewBuilder content: @escaping (_ indexPath: IndexPath, _ item: Item) -> AnyView) {
self.layout = layout
self.sections = sections
self.items = items
self.content = content
}
func makeUIViewController(context: Context) -> CollectionViewController {
let controller = CollectionViewController()
controller.layout = layout
controller.content = content
controller.snapshot = snapshotForCurrentState()
controller.collectionView.delegate = context.coordinator
return controller
}
func updateUIViewController(_ controller: CollectionViewController, context: Context) {
controller.snapshot = snapshotForCurrentState()
controller.reloadDataSource()
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
А вот код CollectionViewController
class CollectionViewController: UIViewController {
var layout: UICollectionViewLayout! = nil
var snapshot: NSDiffableDataSourceSnapshot<Section, Item>! = nil
var content: ((_ indexPath: IndexPath, _ item: Item) -> AnyView)! = nil
let queue = DispatchQueue(label: "diffQueue")
lazy var dataSource: UICollectionViewDiffableDataSource<Section, Item> = {
let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView, cellProvider: cellProvider)
return dataSource
}()
lazy var collectionView: UICollectionView = {
let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
collectionView.backgroundColor = .red //.clear
return collectionView
}()
var isLoaded: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
configureCollectionView()
// load initial data
reloadDataSource()
isLoaded = true
}
}
extension CollectionViewController {
func reloadDataSource(animating: Bool = false) {
dataSource.apply(snapshot, animatingDifferences: animating) {
print("applying snapshot completed!")
}
}
}
extension CollectionViewController {
private func configureCollectionView() {
view.addSubview(collectionView)
collectionView.register(HostingControllerCollectionViewCell<AnyView>.self, forCellWithReuseIdentifier: HostingControllerCollectionViewCell<AnyView>.reuseIdentifier)
collectionView.delegate = self
print("configured collection view")
}
private func cellProvider(collectionView: UICollectionView, indexPath: IndexPath, item: Item) -> UICollectionViewCell? {
print("providing cell for \(indexPath)...")
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HostingControllerCollectionViewCell<AnyView>.reuseIdentifier, for: indexPath) as? HostingControllerCollectionViewCell<AnyView> else {
fatalError("Could not load cell")
}
//cell.host(AnyView(Text(item.title)))
cell.host(content(indexPath, item))
return cell
}
}
Он работает, но я могу загружать источник данных только в viewDidLoad, вызывая reloadDatasource()
, если этот метод затем вызывается снова из updateUIViewController в UIViewControllerRepresentable, тогда представление коллекции остается пустым.
Вот полный репозиторий этой примерной оболочки CollectionView https://github.com/michzio/SwifUICollectionView.
ОБНОВЛЕНИЕ
Я заметил, что получаю эту ошибку
2020-05-02 07:18:20.891685+0200 SwiftUICollectionView[39727:58991470] [CollectionView] Layout attributes <UICollectionViewLayoutAttributes: 0x7fbd6f30b270> index path: (<NSIndexPath: 0xd64d621a2e1d9027> {length = 2, path = 0 - 37}); frame = (187.667 792; 187.667 44); were received from the layout <UICollectionViewCompositionalLayout: 0x7fbd6df04920> but are not valid for the data source counts. Attributes will be ignored.
Я также заметил, что если я изменю в updateUIViewController
controller.reloadDataSource(animating: false)
to
controller.reloadDataSource(animating: true)
ячейки отображаются. Но последовательное обновление, принудительное при касании жестом, приводит к зависанию пользовательского интерфейса (я думаю, что, возможно, элементов слишком много, и с расчетом анимации возникают проблемы, но нет, даже если я уменьшил количество элементов до 1000, он зависает).
ОБНОВЛЕНИЕ 2
Теперь при анимации: true в reloadDataSource () (из updateUIViewController) я могу получить сбои, но нет информации только EXC_BAD_ARITHM
если я удалю начальную загрузку данных в CollectionViewController.viewDidLoad (), тогда вызывается только updateUIViewController, и здесь происходит другое странное исключение
Я забыл сказать, что эти исключения начали происходить, когда я заменил CompositionalLayout простым UICollectionFlowLayout!
Как я вижу, вы не применяете новый снимок в методе updateUIViewController. Должно получиться что-то вроде этого