【问题标题】:contentSize is not updated after reloadData is called on UICollectionView在 UICollectionView 上调用 reloadData 后 contentSize 未更新
【发布时间】: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

标签: ios uicollectionview


【解决方案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 返回始终为零。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多