UICollectionView performBatchUpdates: asserts unexpectedly if view needs layout?

后端 未结 2 1469
野的像风
野的像风 2021-02-09 16:58

If I call -[UICollectionView performBatchUpdates:] from inside viewWillAppear:, inside viewDidAppear:, between these methods, or anytime t

相关标签:
2条回答
  • 2021-02-09 17:46

    UICollectionView seems to have special behavior (a bug?): if it needs layout, then performBatchUpdates: effectively acts as a reloadData before calling the update block, which renders whatever changes you plan to make during the update block poisonous to the collection view bookkeeping.

    If you plan to have batch updates applied to the view before it's been properly laid out (like from a notification handler in a rapidly changing data model environment for instance), you need to make sure that in viewWillAppear: that you call layoutIfNeeded on the collection view. This will prevent the collection view from reloading in the call to performBatchUpdates:.

    We discovered this behavior by putting a log in our collection view data source numberOfSections method and printed out this backtrace to see where it was called from:

    2014-11-12 15:30:06.173 CVCrasher[66830:6387719] [CV] #sections stack: (
    0   CVCrasher     0x000000010ba9122d -[MyViewController numberOfSectionsInCollectionView:] + 61
    1   UIKit         0x000000010cfc2811 -[UICollectionViewData _updateItemCounts] + 147
    2   UIKit         0x000000010cfc4a89 -[UICollectionViewData numberOfSections] + 22
    3   UIKit         0x000000010cfaebae -[UICollectionViewFlowLayout _getSizingInfos] + 348
    4   UIKit         0x000000010cfafca9 -[UICollectionViewFlowLayout _fetchItemsInfoForRect:] + 526
    5   UIKit         0x000000010cfab51f -[UICollectionViewFlowLayout prepareLayout] + 257
    6   UIKit         0x000000010cfc2a10 -[UICollectionViewData _prepareToLoadData] + 67
    7   UIKit         0x000000010cfc30e9 -[UICollectionViewData validateLayoutInRect:] + 54
    8   UIKit         0x000000010cf8b7b8 -[UICollectionView layoutSubviews] + 170
    9   UIKit         0x000000010c9d1973 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 521
    10  QuartzCore    0x0000000110cf2de8 -[CALayer layoutSublayers] + 150
    11  QuartzCore    0x0000000110ce7a0e _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
    12  UIKit         0x000000010c9c5847 -[UIView(Hierarchy) layoutBelowIfNeeded] + 611
    13  UIKit         0x000000010cf9c7b7 -[UICollectionView performBatchUpdates:completion:] + 164
    ...
    

    Here you can plainly see that the call to performBatchUpdates: is hitting the datasource and becoming consistent with the changed model before the updates are applied. When the block itself is then called, the collection view will throw an assert like I showed in the original question.

    tl;dr - When UICollectionView needs layout, performBatchUpdates: effectively acts as a call to reloadData and will make the batch update block assert because of bad bookkeeping. Call layoutIfNeeded in viewWillAppear: to avoid this behavior.

    0 讨论(0)
  • 2021-02-09 17:52

    This is expected behaviour and not a bug. From the documentation on performBatchUpdates:

    If the collection view's layout is not up to date before you call this method, a reload may occur. To avoid problems, you should update your data model inside the updates block or ensure the layout is updated before you call performBatchUpdates(_:completion:).

    0 讨论(0)
提交回复
热议问题