UICollectionView performBatchUpdates:如果视图需要布局,则意外断言?
Posted
技术标签:
【中文标题】UICollectionView performBatchUpdates:如果视图需要布局,则意外断言?【英文标题】:UICollectionView performBatchUpdates: asserts unexpectedly if view needs layout? 【发布时间】:2014-11-12 23:46:24 【问题描述】:如果我从viewWillAppear:
内部、viewDidAppear:
内部、在这些方法之间调用-[UICollectionView performBatchUpdates:]
,或者任何时候集合视图没有被更大的 UIView 视图层次结构布局,集合视图将断言:
*** 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序, 原因:'无效更新:无效的部分数。节数 更新后集合视图中包含的 (X) 必须等于数字 更新前集合视图中包含的部分 (X),加号或减号 插入或删除的节数(X 插入,0 删除)。'
其中“X”是我插入的任何数量的项目。我已经确认数据源正在正确更新,并且更新中的索引路径是正确的。
如果在调用performBatchUpdates:
之前刚刚更新了数据源,那么集合视图状态如何与数据源保持一致?似乎有什么意外触发了 reloadData。
【问题讨论】:
【参考方案1】:UICollectionView 似乎有特殊的行为(一个错误?):如果它需要布局,那么performBatchUpdates:
在调用更新块之前有效地充当reloadData
,这会使您计划在更新块期间进行的任何更改都有毒到集合视图簿记。
如果您计划在视图正确布局之前将批量更新应用到视图(例如从快速变化的数据模型环境中的通知处理程序),您需要确保在您调用的 viewWillAppear:
layoutIfNeeded
在集合视图上。这将防止集合视图在调用 performBatchUpdates:
时重新加载。
我们通过在我们的集合视图数据源numberOfSections
方法中添加一个日志来发现这种行为,并打印出这个回溯以查看它是从哪里调用的:
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
...
在这里您可以清楚地看到对performBatchUpdates:
的调用正在访问数据源并与更改的模型保持一致在应用更新之前。然后调用块本身时,集合视图将抛出一个断言,就像我在原始问题中显示的那样。
tl;dr - 当 UICollectionView 需要布局时,performBatchUpdates:
有效地充当对reloadData
的调用,并且由于记账错误而使批量更新块断言。在viewWillAppear:
中调用layoutIfNeeded
以避免这种行为。
【讨论】:
您的意思是在viewWillAppear:
中调用layoutIfNeeded
并稍后在viewDidAppear:
中调用performBatchUpdates:
吗?所以当在viewDidAppear:
中调用performBatchUpdates:
时,UICollectionView会检查发现不需要再次布局,不会调用reloadData
?
这个该死的错误只花了一天多的时间就解决了。感谢您的回答,为我指明了正确的方向。在viewWillAppear
中执行layoutIfNeeded
对我们不起作用,因为更新开始得更早 - 但在我们的数据模型更新解决它之前执行layoutIfNeeded
!这绝对应该作为一个错误提交给苹果。如果他们决定使用reloadData
而不是批量更新,则必须丢弃批量更新块以避免不一致。【参考方案2】:
这是预期的行为,而不是错误。来自performBatchUpdates
的文档:
如果在调用此方法之前集合视图的布局不是最新的,则可能会发生重新加载。为避免出现问题,您应该在更新块内更新数据模型或确保在调用 performBatchUpdates(_:completion:) 之前更新布局。
【讨论】:
以上是关于UICollectionView performBatchUpdates:如果视图需要布局,则意外断言?的主要内容,如果未能解决你的问题,请参考以下文章
-[UICollectionView _endItemAnimations] 中的 UICollectionView 断言失败
如何滚动另一个 UICollectionView 下方的 UICollectionView?
UICollectionView 单元内部已加载,但 UICollectionView 未显示
UICollectionView 断言失败 -[UICollectionView _updateWithItems:tentativelyForReordering:]
UITableviewCell 中的 UICollectionView。添加约束后,UICollectionView 不显示