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