UICollectionView 不重用单元格

Posted

技术标签:

【中文标题】UICollectionView 不重用单元格【英文标题】:UICollectionView doesn't reuse cell 【发布时间】:2019-07-10 01:15:54 【问题描述】:

我正在尝试使用 UICollectionView 模仿 UITableView 布局。

layout.itemSize = CGSizeMake(CGRectGetWidth(self.view.bounds), 44.0f);

我注册了可重用单元类。

[self.collectionView registerClass:[SampleCell class]
            forCellWithReuseIdentifier:NSStringFromClass([SampleCell class])];

注意:SampleClass 只是 UICollectionViewCell 的子类,它不包含任何内容。

并符合数据源:

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView

    return 1;


- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

    return 28;


- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([SampleCell class])
                                                                           forIndexPath:indexPath];

    return cell;

我发现SampleCell 没有被重用。为了验证它,我们可以简单地在UICollectionView 中记录子视图的数量。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView

    NSLog(@"number of subviews in collection view is: %li", (long)self.collectionView.subviews.count);

滚动后,我得到了这个日志:

集合视图中的子视图数量为:30

集合视图中的子视图数量为:30

集合视图中的子视图数量为:30

集合视图中的子视图数量为:30

请注意,有 30 个子视图(其中 2 个是滚动视图指示器)。

这意味着所有 28 个项目都会显示,而不会从 superview 中删除不可见的单元格。为什么会这样?

为了方便您,我在 Github 上提供了一个示例项目。 https://github.com/edwardanthony/UICollectionViewBug

更新:

我还使用内存图层次结构调试器检查了内存分配,它被分配了 28 次。

【问题讨论】:

【参考方案1】:

我确实在工作,只是由于更积极的缓存而在内存中保留了更多。如果您尝试将项目数从 28 更改为 100,您会看到滚动时它保持在 33 个子视图。

尝试将以下代码添加到您的 SampleCell 类中,您会看到它被调用,但可能与您预期的不太一样。

- (void)prepareForReuse 
    [super prepareForReuse];

    NSLog(@"prepareForReuse called");

UICollectionView 具有比UITableView 更高级的缓存方案(或至少与以前一样),这就是您看到自己所做的事情的原因。根据文档,它说 默认情况下启用单元格预取

UICollectionView 提供了两种预取技术,可用于 提高响应能力:

单元格预取 提前准备单元格 他们需要的时间。当一个集合视图需要一个大的 同时单元格的数量 - 例如,网格中的新行单元格 布局——单元格的请求时间早于 展示。因此,单元格渲染分布在多个布局中 通过,从而获得更流畅的滚动体验。单元格预取 默认启用。 数据预取提供了一种机制 您会收到有关集合视图的数据要求的通知 提前申请单元格。这很有用,如果内容 您的单元依赖于昂贵的数据加载过程,例如 网络请求。分配一个符合 UICollectionViewDataSourcePrefetching 协议到 prefetchDataSource 属性来接收何时通知 预取单元格的数据。

您可以通过将此行添加到示例中的setupCollectionView 函数来关闭单元格预取:

self.collectionView.prefetchingEnabled = NO;

这样做将使您的示例按预期工作。在我的情况下,子视图计数将下降到 18。

【讨论】:

【参考方案2】:

我怀疑计算子视图并不能反映单元重用。可能是子视图包含对同一单元格的多个引用。要计算使用的单元格数量,您可以记录 UICollectionViewCell 子类被初始化的次数。只需覆盖它的 init 方法并在其中放置一个 print 语句。

还有一点需要注意(抱歉,如果它已经很明显了),如果所有单元格都在屏幕上可见,则不会发生重复使用。当单元格在滚动期间离开屏幕时,就会发生单元格重用。

【讨论】:

保存内存的唯一方法是将其从超级视图中删除,这样就不会有层内存开销。这是在UITableView 上完成的。而且我还检查了它在屏幕上不可见,因此应该重复使用。我错过了什么吗? 我想说只是尝试计算初始化单元的数量,这是分配内存的真正衡量标准,超级视图中的视图数量可能无法反映内存使用情况。 感谢您的回复。我对内存分配做了一些检查。你可以看到我更新的帖子。 感谢使用内存图层次结构调试器和探索引擎盖的好奇心!我看不出你的代码有任何问题。啊,现在我看到了 LGP 的评论,所以你必须小心过早的优化,而且 Apple 的 API 没有提供说重用将立即发生的合同。前几个版本的 ios 可能就是这种情况,但现在内存不再那么受限了

以上是关于UICollectionView 不重用单元格的主要内容,如果未能解决你的问题,请参考以下文章

如何不在 UICollectionView 中重用单元格 - Swift

重用单元格 UICollectionView - 某些单元格没有被选中

UICollectionView 重用单元格问题

拖放 UICollectionView 单元格重用问题

在 UICollectionView 上禁用单元格重用

UICollectionView 重用单元格背景问题