Оболочка SwiftUI CollectionView не может применить снимок из updateUIViewController

Я реализовал такую ​​оболочку 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. Должно получиться что-то вроде этого


person Michał Ziobro    schedule 01.05.2020    source источник


Ответы (1)


У меня есть эта функция кода updateUIViewController (_ controller: CollectionViewController, context: Context) {controller.snapshot = snapshotForCurrentState () controller.reloadDataSource ()} Выше я просто вставил ошибочный код. Но с этим reloadDataSource () не работает

func updateUIViewController(_ controller: CollectionViewController, context: Context) {

    controller.snapshot = snapshotForCurrentState()
    controller.reloadDataSource()
}
person Alexander Gaidukov    schedule 01.05.2020
comment
* Ошибка утверждения в - [UICollectionView _dequeueReusableViewOfKind: withIdentifier: forIndexPath: viewCategory:], /Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3920.26.113/UICollectionView.m-5971 2020 02 07: 33: 02.037804 + 0200 SwiftUICollectionView [40091: 59004149] * Завершение работы приложения из-за неперехваченного исключения 'NSInternalInconsistencyException', причина: 'не удалось удалить из очереди вид вида: UICollectionElementKindCell с идентификатором ItemCell - необходимо зарегистрировать перо или класс для идентификатора или подключите ячейку прототипа в раскадровке '*** Стек вызовов первого вызова: (0 CoreFoundation 0x00007fff23e39f0e exceptionPreprocess + 350 1 libobjc.A.dylib
0x00007fff50ad79b2 objc_exception_throw + 48 2 CoreFoundation
0x00007fff23e39c88 + [NSException raise: format: arguments:] + 88 3
Foundation 0x00007fff258a3cd2 - [NSAssertionHandler handleFailureInMethod: object: file: lineNumber : Описание:] + 191 4
UIKitCore 0x00007fff4838b36e - [UICollectionView _dequeueReusableViewOfKind: withIdentifier: forIndexPath: viewCategory:] + 2426 5 UIKitCore 0x00007fff4838b53d - [UICollectionView dequeueReusableCellWithReuseIdentifier: forIndexPath:] + 88 6
SwiftUICollectionView 0x0000000108b5fcb3 $ s21SwiftUICollectionView010CollectionC10ControllerC12cellProvider33_7499D878310ABE7B5F37FEF32561A438LL010collectionC09indexPath4itemSo0bC4CellCSgSo0bC0C_10Foundation05IndexP0VAA4ItemCtF + 867 7 SwiftUICollectionView 0x0000000108b62ad0 $ s21SwiftUICollectionView010CollectionC10ControllerC12cellProvider33_7499D878310ABE7B5F37FEF32561A438LL010collectionC09indexPath4itemSo0bC4CellCSgSo0bC0C_10Foundation05IndexP0VAA4ItemCtFTA + 16 8 SwiftUICollectionView 0x0000000108b5ffaf $ sSo16UICollectionViewC10Foundation9IndexPathV05SwiftaB04ItemCSo0aB4CellCSgIeggngo_AbehKIeggn no_TR + 15 9 libswiftUIKit.dylib 0x00007fff5170a8ce $ s5UIKit29UITableViewDiffableDataSourceC05tableC012cellProviderACyxq_GSo0bC0C_So0bC4CellCSgAH_10Foundation9IndexPathVq_tctcfcAkH_ANyptcfU_ + 126 10 libswiftUIKit.dylib 0x00007fff5170a998 $ sSo11UITableViewC10Foundation9IndexPathVypSo0aB4CellCSgIeggnno_ABSo07NSIndexE0CyXlAHIeyByyya_TR + 168 11 UIKitCore 0x00007fff48342a89 - [__ UIDiffableDataSource CollectionView: cellForItemAtIndexPath:] + 165 12 UIKitCore 0x00007fff483432d5 - [__ UIDiffableDataSource _cellForItemAtIndexPath: CollectionView:] + 50 13 libswiftUIKit. dylib 0x00007fff5170b036 $ s5UIKit34UICollectionViewDiffableDataSourceC010collectionC0_13cellForItemAtSo0bC4CellCSo0bC0C_10Foundation9IndexPathVtFTm + 70 14 libswiftUIKit.dylib 0x00007fff5170b110 $ s5UIKit34UICollectionViewDiffableDataSourceC010collectionC0_13cellForItemAtSo0bC4CellCSo0bC 0C_10Foundation9IndexPathVtFToTm + 128 15 UIKitCore 0x00007fff483759d5 - [UICollectionView _createPreparedCellForItemAtIndexPath: withLayoutAttributes: applyAttributes: IsFocused: уведомить:] + 416 16 UIKitCore 0x00007fff4837582f - [UICollectionView _createPreparedCellForItemAtIndexPath: withLayoutAttributes: applyAttributes:] + 31 17 UIKitCore 0x00007fff48395e4e __51- [UICollectionView _viewAnimationsForCurrentUpdate] _block_invoke. 1814 + 661 18 UIKitCore 0x00007fff483929e4 - [UICollectionView _viewAnimationsForCurrentUpdate] + 3213 19 UIKitCore 0x00007fff48399cde __71- [UICollectionView _updateWithItems: tentativelyForReordering: аниматора:] _ block_invoke.1887 + 118 20 UIKitCore 0x00007fff490bb8a8 + [UIView (анимация) performWithoutAnimation:] + 84 21 UIKitCore 0x00007fff48398b24 - [UICollectionView _updateWithItems: tentativelyForReordering: аниматора:] + 4003 22 UIKitCore 0x00007fff48391689 - [UICollectionView _endItemAnimationsWithInvalidationContext: tentativelyForReordering: аниматора:] + 16761 23 UIKitCore 0x00007fff4839b0e6 - [UICollectionView _endUpdatesWithInvalidationContext: tentativelyForReordering: аниматора:] + 71 24 UIKitCore 0x00007fff4839b445 - [UICollectionView _performBatchUpdates: завершение : недействительность Контекст: tentativelyForReordering: аниматора:] + 462 25 UIKitCore 0x00007fff4839b254 - [UICollectionView _performBatchUpdates: завершение: invalidationContext: tentativelyForReordering:] + 90 26 UIKitCore 0x00007fff4839b1d7 - [UICollectionView _performBatchUpdates: завершение: invalidationContext:] + 74 27 UIKitCore 0x00007fff4839b12c - [UICollectionView performBatchUpdates: завершение :] + 53 28 UIKitCore 0x00007fff483aa9ef - [UICollectionView _performDiffableUpdate:] + 44 29 UIKitCore 0x00007fff4834906e - [_ UIDiffableDataSourceViewUpdater _performUpdateWithCollectionViewUpdateItems: dataSourceSnapshotter: updateHandler: завершение:] + 467 30 UIKitCore 0x00007fff4834213a - [__ UIDiffableDataSource _commitNewDataSource: withViewUpdates: завершение:] + 246 31 UIKitCore 0x00007fff4833cb22 __66- [__UIDiffableDataSource applyDifferencesFromSnapshot: завершение:] _ block_invoke.154 + 190 32 UIKitCore 0x00007fff4833cdb1 __66 - [__ UIDiffableDataSource applyDifferencesFromSnapshot: завершение:] _ block_invoke.180 + 107 33 libdispatch.dylib 0x0000000108e01e8e _dispatch_client_callout + 8 34 libdispatch.dylib 0x0000000108e10ae2 _dispatch_lane_barrier_sync_invoke_and_complete + 132 35 UIKitCore 0x00007fff4833c62b - [ __UIDiffableDataSource applyDifferencesFromSnapshot: завершение:] + 952 36 UIKitCore 0x00007fff4833d63d - [__ UIDiffableDataSource applyDifferencesFromSnapshot: animatingDifferences: завершение:] + 71 37 libswiftUIKit.dylib 0x00007fff5170aaf4 $ s5UIKit34UICollectionViewDiffableDataSourceC5apply_20animatingDifferences10completionyAA010NSDiffableeF8SnapshotVyxq_G_SbyycSgtFTm + 212 38 SwiftUICollectionView 0x0000000108b620b9 $ s21SwiftUICollectionView010CollectionC10ControllerC16reloadDataSource8snapshot9animatingy5UIKit010NSDiffablegH8SnapshotVyAA7SectionOAA4ItemCG_SbtF + 985 39 SwiftUICollectionView 0x0000000108b5682f $ s21SwiftUICollectionView010CollectionC0V22updateUIViewController_7contextyAA0dcG0C_0A2UI0fG20RepresentableContextVyACGtF + 335 40 SwiftUICollectionView 0x0000000108b56a2b $ s21SwiftUICollectionView010CollectionC0V0A2UI29UIViewControllerRepresentableAadEP06updatefG0_7contexty0fG4TypeQz_AD0fgH7ContextVyxGtFTW + 59 41 SwiftUI 0x00007fff2c59a1b2 $ s7SwiftUI42PlatformViewControllerRepresentableAdaptorV06updateD8Provider_7contexty06UIViewE4TypeQz_AA0cdF7ContextVyACyxGGtF + 290 42 SwiftUI 0x00007fff2c670439 $ s7SwiftUI17PlatformViewChild33_A513612C07DFA438E70B9FA90719B40DLLV6update7contexty14AttributeGraph0O7ContextVyADyxGGz_tFyyXEfU_yyXEfU_ + 217 43 SwiftUI 0x00007fff2c7f5e20 $ s7SwiftUI16ViewRendererHostPAAE21performExternalUpdateyyyyXEF + 192 44 SwiftUI 0x00007fff2c66f810 $ s7SwiftUI17PlatformViewChild33_A513612C07DFA438E70B9FA90719B40DLLV6update7contexty14AttributeGraph0O7ContextVyADyxGGz_tFyyXEfU_7performL_4workyyyXE_tAA0cD13RepresentableRzlF + 224 45 SwiftUI 0x00007fff2c66e6b6 $ s7SwiftUI17PlatformViewChild33_A513612C07DFA438E70B9FA90719B40DLLV6update7contexty14AttributeGraph0O7ContextVyADyxGGz_tFyyXEfU_ + 2454 46 SwiftUI 0x00007fff2c6674ae $ s7SwiftUI17PlatformViewChild33_A513612C07DFA438E70B9FA90719B40DLLV6update7contexty14AttributeGraph0O7ContextVyADyxGGz_tF + 590 47 SwiftUI 0x00007fff2c670940 $ s7SwiftUI17PlatformViewChild33_A513612C07DFA438E70B9FA90719B40DLLVyxG14AttributeGraph07UntypedM0AafGP7_update_5graph9attributeySv_So10AGGraphRefaSo11AGAttributeatFZTW - person Michał Ziobro; 01.05.2020