UICollectionView 不重用单元格
UICollectionView doesn't reuse cell
我正在尝试使用 UICollectionView 模仿 UITableView 布局。
layout.itemSize = CGSizeMake(CGRectGetWidth(self.view.bounds), 44.0f);
我注册了可重复使用的单元class。
[self.collectionView registerClass:[SampleCell class]
forCellWithReuseIdentifier:NSStringFromClass([SampleCell class])];
注意:SampleClass
只是 UICollectionViewCell
的子class,不包含任何内容。
并符合数据来源:
- (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);
}
滚动后,我得到了这个日志:
number of subviews in collection view is: 30
number of subviews in collection view is: 30
number of subviews in collection view is: 30
number of subviews in collection view is: 30
注意有 30 个子视图(其中 2 个是滚动视图指示器)。
这意味着显示了所有 28 个项目,而没有从 superview 中删除不可见的单元格。为什么会这样?
为了方便您,我在 Github 上提供了一个示例项目。
https://github.com/edwardanthony/UICollectionViewBug
更新:
我还使用内存图层次调试器检查了内存分配,它分配了 28 次。
我怀疑计算子视图并不能反映单元重用。可能是子视图包含对同一单元格的多个引用。要计算使用的单元格数量,您可以记录 UICollectionViewCell 子类被初始化的次数。只需覆盖它的 init 方法并在其中放置一个打印语句。
还有一点要注意(很抱歉,如果它已经很明显),如果所有单元格都在屏幕上可见,则不会发生重用。当单元格在滚动过程中离开屏幕时,就会重复使用单元格。
我确实在工作,只是由于更积极的缓存而在内存中保留了一点。如果您尝试将项目数从 28 更改为 100,您将看到滚动时它停留在 33 个子视图。
尝试将以下代码添加到您的 SampleCell
class 中,您会看到它被调用,但可能与您预期的不一样。
- (void)prepareForReuse {
[super prepareForReuse];
NSLog(@"prepareForReuse called");
}
UICollectionView
具有比 UITableView
更高级的缓存方案(或者至少它曾经有),这就是你看到你所做的事情的原因。根据文档,它说 Cell 预取是默认启用的:
UICollectionView provides two prefetching techniques you can use to
improve responsiveness:
Cell prefetching prepares cells in advance of
the time they are required. When a collection view requires a large
number of cells simultaneously—for example, a new row of cells in grid
layout—the cells are requested earlier than the time required for
display. Cell rendering is therefore spread across multiple layout
passes, resulting in a smoother scrolling experience. Cell prefetching
is enabled by default.
Data prefetching provides a mechanism whereby
you are notified of the data requirements of a collection view in
advance of the requests for cells. This is useful if the content of
your cells relies on an expensive data loading process, such as a
network request. Assign an object that conforms to the
UICollectionViewDataSourcePrefetching protocol to the
prefetchDataSource property to receive notifications of when to
prefetch data for cells.
您可以通过将此行添加到示例中的 setupCollectionView
函数来关闭单元格预取:
self.collectionView.prefetchingEnabled = NO;
这样做将使您的示例按预期工作。在我的例子中,子视图数将下降到 18。
我正在尝试使用 UICollectionView 模仿 UITableView 布局。
layout.itemSize = CGSizeMake(CGRectGetWidth(self.view.bounds), 44.0f);
我注册了可重复使用的单元class。
[self.collectionView registerClass:[SampleCell class]
forCellWithReuseIdentifier:NSStringFromClass([SampleCell class])];
注意:SampleClass
只是 UICollectionViewCell
的子class,不包含任何内容。
并符合数据来源:
- (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);
}
滚动后,我得到了这个日志:
number of subviews in collection view is: 30
number of subviews in collection view is: 30
number of subviews in collection view is: 30
number of subviews in collection view is: 30
注意有 30 个子视图(其中 2 个是滚动视图指示器)。
这意味着显示了所有 28 个项目,而没有从 superview 中删除不可见的单元格。为什么会这样?
为了方便您,我在 Github 上提供了一个示例项目。 https://github.com/edwardanthony/UICollectionViewBug
更新:
我还使用内存图层次调试器检查了内存分配,它分配了 28 次。
我怀疑计算子视图并不能反映单元重用。可能是子视图包含对同一单元格的多个引用。要计算使用的单元格数量,您可以记录 UICollectionViewCell 子类被初始化的次数。只需覆盖它的 init 方法并在其中放置一个打印语句。
还有一点要注意(很抱歉,如果它已经很明显),如果所有单元格都在屏幕上可见,则不会发生重用。当单元格在滚动过程中离开屏幕时,就会重复使用单元格。
我确实在工作,只是由于更积极的缓存而在内存中保留了一点。如果您尝试将项目数从 28 更改为 100,您将看到滚动时它停留在 33 个子视图。
尝试将以下代码添加到您的 SampleCell
class 中,您会看到它被调用,但可能与您预期的不一样。
- (void)prepareForReuse {
[super prepareForReuse];
NSLog(@"prepareForReuse called");
}
UICollectionView
具有比 UITableView
更高级的缓存方案(或者至少它曾经有),这就是你看到你所做的事情的原因。根据文档,它说 Cell 预取是默认启用的:
UICollectionView provides two prefetching techniques you can use to improve responsiveness:
Cell prefetching prepares cells in advance of the time they are required. When a collection view requires a large number of cells simultaneously—for example, a new row of cells in grid layout—the cells are requested earlier than the time required for display. Cell rendering is therefore spread across multiple layout passes, resulting in a smoother scrolling experience. Cell prefetching is enabled by default.
Data prefetching provides a mechanism whereby you are notified of the data requirements of a collection view in advance of the requests for cells. This is useful if the content of your cells relies on an expensive data loading process, such as a network request. Assign an object that conforms to the UICollectionViewDataSourcePrefetching protocol to the prefetchDataSource property to receive notifications of when to prefetch data for cells.
您可以通过将此行添加到示例中的 setupCollectionView
函数来关闭单元格预取:
self.collectionView.prefetchingEnabled = NO;
这样做将使您的示例按预期工作。在我的例子中,子视图数将下降到 18。