在 UICollectionView 上调用 reloadData 后 contentSize 未更新

Posted

技术标签:

【中文标题】在 UICollectionView 上调用 reloadData 后 contentSize 未更新【英文标题】:contentSize is not updated after reloadData is called on UICollectionView 【发布时间】:2013-09-30 20:35:03 【问题描述】:

有谁知道为什么在 UICollectionView 上调用 reloadData 后 contentSize 没有立即更新?

如果您需要知道 contentSize,我发现的最佳解决方法如下:

[_collectionView reloadData];

double delayInSeconds = 0.0001;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^(void)
    
        // TODO: Whatever it is you want to do now that you know the contentSize.
    );

显然,这是一个相当脆弱的 hack,它对 Apple 的实现做出假设,但到目前为止,它已被证明非常可靠。

是否有人对为什么会发生这种情况有任何其他解决方法或知识?我正在辩论提交雷达,因为我不明白为什么他们不能在同一个运行循环中计算 contentSize。这就是 UITableView 在其整个实现中的工作方式。

编辑:这个问题用于引用块内的 setContentOffset 方法,因为我想在我的应用程序中滚动集合视图。我删除了方法调用,因为人们的答案集中在为什么我没有在 contentSize 没有更新的原因中使用 scrollToItemAtIndexPath。

【问题讨论】:

我认为更合适的方法是使用 scrollToItemAtIndexPath:atScrollPosition:animated:。至于为什么不更新内容大小,我只能假设它是一种优化,以防止调用潜在的大量且昂贵的动态大小计算或返回不准确的信息,进而进行不必要的处理和工作。 在实际的应用程序中我确实使用了它。为简单起见,我选择提及 setContentOffset。这是一个很好的观点,即多次调用 reloadData 可能会触发昂贵的计算。太糟糕了,没有带有完成处理程序方法的 reloadData。 在访问UICollectionView 之前致电layoutIfNeeded contentSize 【参考方案1】:

要获取重新加载后的内容大小,请尝试调用布局对象的collectionViewContentSize。这个对我有用。

【讨论】:

一开始我很怀疑,但它也对我有用 :) 看起来对 reloadData 的调用不会立即触发集合视图以更改其渲染属性,但就像 Clement 建议的那样,您可以直接转到布局对象以立即获取此信息。 SWIFT 3:让 contentSize = self.myCollectionView.collectionViewLayout.collectionViewContentSize 这也解决了我的问题 :) 调用执行批量更新以获取内容高度并发生崩溃,但我的主要问题是内容高度尚不存在。但我的布局有适当的高度。【参考方案2】:

这对我有用:

[self.collectionView.collectionViewLayout invalidateLayout];
[self.collectionView.collectionViewLayout prepareLayout];

【讨论】:

结合 layoutIfNeeded 对我有用。 这对我有用。我从自我调整大小的单元格中得到了这个错误,在 ios 10 及之前的 reloadData 之后 contentSize 不会更新。在 iOS 11 上我不需要这样做。 @x.y,你是如何在 iOS 11 中修复它的?它正常工作吗?【参考方案3】:

编辑:我刚刚对此进行了测试,实际上,当数据更改时,我的原始解决方案将崩溃​​,并出现以下情况:

“无效更新:第0节中的项目数无效。更新后现有节中包含的项目数(7)必须等于更新前该节中包含的项目数(100),加上或减去从该部分插入或删除的项目数(0 插入,0 删除)加上或减去移入或移出该部分的项目数(0 移入,0 移出)。"

处理这个问题的正确方法是在数据源发生变化时计算插入、删除和移动,并在发生变化时围绕它们使用 performBatchUpdates。例如,如果将两项添加到作为数据源的数组的末尾,则代码如下:

NSArray *indexPaths = @[indexPath1, indexPath2];
[self.collectionView performBatchUpdates:^() 

    [self.collectionView insertItemsAtIndexPaths:indexPaths];
 completion:^(BOOL finished) 
    // TODO: Whatever it is you want to do now that you know the contentSize.
];

以下是 Kernix 提供的我的原始答案的编辑解决方案,我认为它不能保证有效。

尝试在 UICollectionView 上执行 performBatchUpdates:completion:。您应该可以访问完成块中的更新属性。它看起来像这样:

[self.collectionView reloadData];
[self.collectionView performBatchUpdates:^() 


 completion:^(BOOL finished) 
    // TODO: Whatever it is you want to do now that you know the contentSize.
];

【讨论】:

这看起来确实是一个可靠的解决方案。更改会产生动画,这不是我想要的,但我确信必须有一种方法可以杀死动画。 您可以使用 initialLayoutAttributesForAppearingItemAtIndexPath: 覆盖布局子类中的初始布局属性。将那里的框架设置为发生变化的单元格的框架。 我很确定这会崩溃,你应该把 reloadData 放到 performBatch 块之外。【参考方案4】:

调用后

[self.collectionView reloadData];

使用这个:

[self.collectionView setNeedsLayout];
[self.collectionView layoutIfNeeded];

那么你就可以得到真正的contentSize

【讨论】:

【参考方案5】:

先调用prepareLayout,然后你会得到正确的contentSize:

[self.collectionView.collectionViewLayout prepareLayout];

【讨论】:

不正确。正如 Apple 所说,默认实现什么都不做:developer.apple.com/library/prerelease/ios/documentation/UIKit/… 同意@CanPoyrazoğlu 这很有趣 - 我有一个案例,调用 invalidateLayout 后跟 reloadData 不会触发 sizeForItem: 调用。但是,在无效和重新加载之间直接调用 prepareLayout 会导致在重新加载期间重新计算大小,正如我所期望的那样......文档确实说实现是空的,但我不确定我是否完全相信这一点...... @CanPoyrazoğlu 文档说UICollectionViewLayout 的方法定义为空。在许多情况下,您将使用默认子类UICollectionViewFlowLayout,它似乎没有空实现,以及为什么它适用于大多数情况。【参考方案6】:

在我的例子中,我有一个自定义布局类,它继承 UICollectionViewFlowLayout 并仅在 Interface Builder 中设置它。我不小心从项目中删除了该类,但 Xcode 没有给我任何错误,并且集合视图的 contentSize 返回始终为零。

我把课放回去了,它又开始工作了。

【讨论】:

以上是关于在 UICollectionView 上调用 reloadData 后 contentSize 未更新的主要内容,如果未能解决你的问题,请参考以下文章

同时在多个 UICollectionView 上调用 reloadData

在 UICollectionView 上调用 reloadData 后 contentSize 未更新

完成加载后,在 UICollectionView 中的第一个单元格上调用函数

在 UITextView 上点击时未调用 UICollectionView didSelectItemAtIndexPath

UICollectionView 标头仅在第一个单元格上调用一次

UICollectionView 的滚动性能不佳 - 分析指向可访问性调用