Ios 在UICollectionView上调用重载数据后,不会更新contentSize

Ios 在UICollectionView上调用重载数据后,不会更新contentSize,ios,uicollectionview,Ios,Uicollectionview,有人知道为什么在UICollectionView上调用reloadData后contentSize不会立即更新吗 如果您需要了解内容大小,我发现最好的解决方法如下: [_collectionView reloadData]; double delayInSeconds = 0.0001; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); di

有人知道为什么在UICollectionView上调用reloadData后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.
    });
显然,这是一个相当脆弱的黑客行为,对苹果的实现做出了假设,但到目前为止,它已经被证明是相当可靠的

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


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

编辑:我刚刚测试了这一点,事实上,当数据更改时,我的原始解决方案将崩溃,原因如下:

[_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.
    });
无效更新:节0中的项目数无效。更新后现有节中包含的项目数(7)必须等于更新前该节中包含的项目数(100),加上或减去从该节插入或删除的项目数(0插入,0删除)加上或减去移入或移出该部分的项目数(0移入,0移出)。”

正确的处理方法是在数据源更改时计算插入、删除和移动,并在数据源更改时使用performbatchUpdate。例如,如果将两项添加到作为数据源的数组末尾,则代码如下:

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上执行更新:完成:操作。您应该可以访问完成块中更新的属性。它看起来是这样的:

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

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

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

首先调用prepareLayout,然后您将获得正确的contentSize:

[self.collectionView.collectionViewLayout prepareLayout];
这对我很有用:

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

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

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

打电话后

[self.collectionView reloadData];
使用以下命令:

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

然后你可以得到真正的
contentSize

我认为更合适的方法是使用scrollToItemAtIndexPath:atScrollPosition:animated:。至于为什么不更新内容大小,我只能假设这是一种优化,以防止调用可能大量且昂贵的动态大小计算或返回不准确的信息,进而进行不必要的处理和工作。在实际应用中,我确实使用了这一点。为了简单起见,我选择提及setContentOffset。这是一个很好的观点,多次调用重载数据可能会触发昂贵的计算。太糟糕了,没有使用完成处理程序重新加载数据的方法。在访问其
contentSize
之前,在
UICollectionView
上调用
layoutifneedd
,这看起来确实像一个解决方案。这些更改将生成动画,这不是我想要的,但我确信一定有办法终止动画。您可以使用initialLayoutAttributesForAppearingItemAtIndexPath:覆盖布局子类中的初始布局属性。将框架设置为发生变化的单元格的框架。我很确定这会崩溃,您应该将重新加载的数据从performBatch块中取出。起初我对此表示怀疑,但它对我也起了作用:)看起来对重新加载数据的调用不会立即触发集合视图更改其渲染属性,但正如Clement建议的那样,您可以直接转到布局对象以立即获取此信息。SWIFT 3:让contentSize=self.myCollectionView.collectionViewLayout.CollectionViewContentSize这也解决了我的问题:)正在调用执行批处理更新以获取内容高度,但我的主要问题是内容高度还不存在。但是我的布局有正确的高度。不正确。默认实现没有任何作用,正如苹果所说:同意@CanPoyrazoğluThis是有趣的——我有一个案例,在调用invalidateLayout之后再调用reloadData不会触发sizeForItem:call。但是,在invalidate和reload之间直接调用prepareLayout会导致在重新加载期间重新计算大小,正如我所期望的那样。。。文档确实说实现是空的,但我不确定我是否完全相信……@CanPoyrazoğlu文档说,
UICollectionViewLayout
的方法定义是空的。在许多情况下,您将使用默认的子类,
UICollectionViewFlowLayout
,它似乎没有空的实现,以及为什么它对大多数人有效。与LayoutIfNeed的组合对我有效。这对我有效。我从自调整单元格中得到了这个bug,contentSize在IOS10和之前重新加载数据后不会更新。在iOS 11上,我不需要这样做。@x.y,你是如何在iOS 11中修复它的?它只是起作用了吗?