Оболочка 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)

к

controller.reloadDataSource(animating: true)

ячейки отображаются. Но последовательное обновление, принудительное при касании жестом, приводит к зависанию пользовательского интерфейса (я думаю, что, возможно, элементов слишком много и с расчетом анимации возникают проблемы, но нет, даже если я уменьшил количество элементов до 1000, он зависает).

ОБНОВЛЕНИЕ 2

Теперь при анимации: true в reloadDataSource() (из updateUIViewController) я могу получить сбои, но нет информации только EXC_BAD_ARITHM

если я удалю начальную загрузку данных в CollectionViewController.viewDidLoad(), тогда вызывается только updateUIViewController, и здесь происходит еще одно странное исключение

* Ошибка утверждения в -[UICollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:], /Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3920.26.113/UICollectionView.m0-05:5971 202033: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: формат: аргументы:] + 88 3
Foundation 0x00007fff258a3cd2 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191 4
UIKitCore 0x00007fff4838b36e: + 191 4 UIKitCore: UIKitCore 0x00007fff4838b53d -[UICollectionView dequeueReusableCellWithReuseIdentifier:forIndexPath:] + 88 6
SwiftUICollectionView 0x0000000108b5fcb3 $s21SwiftUICollectionView010CollectionC10ControllerC12cellProvider33_7499D878310ABE7B5F37FEF32561A438LL010collectionC09indexPath4itemSo0bC4CellCSgSo0bC0C_10Foundation05IndexP0VAA4ItemCtF + 867 7 SwiftUICollectionView 0x0000000108b62ad0 $s21SwiftUICollectionView010CollectionC10ControllerC12cellProvider33_7499D878310ABE7B5F37FEF32561A438LL010collectionC09indexPath4itemSo0bC4CellCSgSo0bC0C_10Foundation05IndexP0VAA4ItemCtFTA + 16 8 SwiftUICollectionView 0x0000000108b5ffaf $sSo16UICollectionViewC10Foundation9IndexPathV05SwiftaB04ItemCSo0aB4CellCSgIeggngo_AbehKIeggnno_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_13cellForItemAtSo0bC4CellCSo0bC0C_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:animator:] + 4003 22 UIKitCore 0x00007fff48391689 -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:] + 16761 23x0000efforetext:] + 16761 23x0000efforetext:]аниматора:] + 71 24 UIKitCore 0x00007fff4839b445 -[UICollectionView _performBatchUpdates: завершение: invalidationContext: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 lib_calloutpatch + 8 34.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 + 32 48 AttributeGraph 0x00007fff2fc46309 $ STA + 25 49 AttributeGraph 0x00007fff2fc2ed45 _ZN2AG5Graph11UpdateStack6updateEv + 455 50 AttributeGraph 0x00007fff2fc2f253 _ZN2AG5Graph16update_attributeEjb + 373 51 AttributeGraph 0x00007fff2fc33d5b _ZN2AG8Subgraph6updateEj + 729 52 SwiftUI 0x00007fff2c51d690 $s7SwiftUI9ViewGraphC14runTransaction33_D63C4EB7F2B205694B6515509E76E98BLL2inySo10AGGraphRefa_tF + 224 53 SwiftUI 0x00007fff2c51da67 $s7SwiftUI9ViewGraphC13updateOutputs2atyAA4TimeV_tFSb5prefs_Sb9idealSizeAC0F0V7outputstSo10AGGraphRefaXEfU_ + 103 54 SwiftUI 0x00007fff2c51d74d $s7SwiftUI9ViewGraphC13updateOutputs2atyAA4TimeV_tF + 125 55 SwiftUI 0x00007fff2c80146b $s7SwiftUI16ViewRendererHostPAAE6render8interval17updateDisplayListySd_SbtFyyXEfU_yyXEfU_ + 811 56 SwiftUI 0x00007fff2c8008c3 $s7SwiftUI16ViewRendererHostPAAE6render8interval17updateDisplayListySd_SbtFyyXEfU_ + 547 57 SwiftUI 0x00007fff2c7f6415 $s7SwiftUI16ViewRendererHostPAAE6render8interval17updateDisplayListySd_SbtF + 373 58 SwiftUI 0x00007fff2c956102 $s7SwiftUI14_SwiftUIIHostingView 59
0x00007fff2c956125 $s7SwiftUI14_UIHostingViewC14layoutSubviewsyyFTo + 21 60 UIKitCore 0x00007fff490c9848 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2478 61 CALayerDelegate layoutSublayersOfLayer:] + 2478 61 QuartzforeCore2 - 62000000 Quartzforecore2 - 0x40000 Layer:]
0x00007fff2b4b457b _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 523 63 QuartzCore 0x00007fff2b4bfc12 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 80 64 QuartzCore 0x00007fff2b408c84 _ZN2CA7Context18commit_transactionEPNS_11TransactionEd + 324 65 QuartzCore 0x00007fff2b43c65f _ZN2CA11Transaction6commitEv + 649 66 UIKitCore 0x00007fff48bdfc2b __34-[UIApplication _firstCommitBlock]_block_invoke_2 + 81 67 CoreFoundation 0x00007fff23d9dcdc __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK
+ 12 68 CoreFoundation 0x00007fff23d9d3d3 __CFRunLoopDoBlocks + 195 69 CoreFoundation 0x00007fff23d981c3 __CFRunLoopRun + 995 70 CoreFoundation
0x00007fff23d97ac4 CFRunLoopRunSpecific + 404 71 GraphicsServices
0x00007fff38b2fc1a GSEventRunModal + 139 72 UIKitCore
0x00007fff48bc7f80 UIApplicationMain + 1605 73 SwiftUICollectionView 0x0000000108b548fb основной + 75 74 libdyld.dylib
0x00007fff519521fd старт + 1)

Я забыл сказать, что эти исключения начали происходить, когда я заменил CompositionalLayout простым UICollectionFlowLayout!

1 ответ

Как я вижу, вы не применяете новый снимок в методе updateUIViewController. Должно получиться что-то вроде этого

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

    controller.snapshot = snapshotForCurrentState()
    controller.reloadDataSource()
}
Другие вопросы по тегам