在滚动期间重用UICollectionViewCells

我有一个问题,

我有一个简单的UICollectionView与静态200单元格,从Flickr加载图像。

我的CellForItemAtIndexPath看起来像这样:

- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"FlickrCell" forIndexPath:indexPath]; cell.backgroundColor = [self generateRandomUIColor]; if(![[cell.subviews objectAtIndex:0] isKindOfClass:[PFImageView class]]) { NSURL *staticPhotoURL = [self.context photoSourceURLFromDictionary:[self.photos objectAtIndex:indexPath.row] size:OFFlickrSmallSize]; PFImageView *imageView = [[PFImageView alloc] initWithFrame:CGRectMake(0, 0, cell.frame.size.height, cell.frame.size.width) andImageURL:staticPhotoURL andOwningCell:cell]; [cell addSubview:imageView]; } return cell; } 

PFImageView是UIImageView一个子类,它在后台线程上加载一个Flickr照片URL,然后在主线程上更新它自己的图像 – 这工作正常。

逻辑非常简单 – 如果没有一个可出队的话,我创build一个单元格。

如果单元格(我期望被出队并且已经有一个PFImageView) 没有一个PFImageView,我分配并初始化一个imageView的单元格,并将其添加为单元格的子视图。

因此,我期望如果单元格已经出队,它应该已经有一个PFImageView作为子视图,因为我们不应该进入if语句来创build一个新的imageView,并启动一个新的照片下载请求

相反,我所看到的是,UICollectionView顶部和底部的单元“暂时离开屏幕” – 当它们回到屏幕上时,它们不会被重用,看起来新的单元格被创build并且图片刷新。

1)一旦单元格被创build,我怎样才能获得静态图像(即当单元格稍微离开屏幕时不刷新。

2)为什么细胞不被重复使用?

非常感谢您的时间。

约翰

我看到几个潜在的问题:

  1. 您正在专门查找您正在添加的子类的单元格的索引0。 UICollectionViewCell可能有其他的视图,所以你不能假设唯一的(或第一个)孩子是你添加的。

  2. 我没有看到你正在调用registerClass:forCellWithReuseIdentifier:或者registerNib:forCellWithReuseIdentifier :,其中一个是正确使用dequeue所必需的( https://developer.apple.com/library/ios/documentation/uikit/reference/ UICollectionViewCell_class / Reference / Reference.html )。

  3. 您只需设置PFImageView的URL就可以构buildPFImageView。 使可重用视图出列的想法是,您将只构造所需视图的一小部分,并且UITableView将在它们移出屏幕时回收它们。 即使不构build新内容,您也需要重置正在请求的indexPath的值。

如果您的情况如您所描述的那样简单,那么您可能会将您的PFImageView添加到已出队的UICollectionView的contentView属性中。

在你的控制器中:

 // solve problem 2 [self.collectionView registerClass:[UICollectionViewCell class] forReuseIdentifer:@"FlickrCell"]; 

在collectionView:cellForItemAtIndexPath

 UICollectionViewCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"FlickrCell" forIndexPath:indexPath]; cell.backgroundColor = [self generateRandomUIColor]; // solve problem 1 by looking in the contentView for your subview (and looping instead of assuming at 0) PFImageView *pfImageView = nil; for (UIView *subview in cell.contentView.subviews) { if ([subview isKindOfClass:[PFImageView class]]) { pfImageView = (PFImageView *)subview; break; } } NSURL *staticPhotoURL = [self.context photoSourceURLFromDictionary:[self.photos objectAtIndex:indexPath.row] size:OFFlickrSmallSize]; if (pfImageView == nil) { // No PFImageView, create one // note the use of contentView! pfImageView = [[PFImageView alloc] initWithFrame:CGRectMake(0, 0, cell.contentView.frame.size.height, cell.frame.size.width) andImageURL:staticPhotoURL andOwningCell:cell.contentView]; [cell.contentView addSubview:pfImageView]; } else { // Already have recycled view. // need to reset the url for the pfImageView. (Problem 3) // not sure what PFImageView looks like so this is an eg I'd probably remove the // URL loading from the ctr above and instead have a function that loads the // image. Then, you could do this outside of the if, regardless of whether you had // to alloc the child view or not. [pfImageView loadImageWithUrl:staticPhotoURL]; // if you really only have 200 static images, you might consider caching all of them } return cell; 

对于不那么简单的情况(例如,我想在视觉上布局单元格,或者在内容中有多个孩子的地方),我通常使用Interface Builder自定义我的UICollectionViewCell。

  1. 在项目中创build一个UICollectionViewCell的子类(在你的情况下,称之为PFImageCell)。
  2. 添加一个IBOutlet属性到我想在初始化中改变的视图的子类(在你的情况下,一个UIImageView)。 @property (nonatomic, assign) IBOutlet UIImageView *image;
  3. 在Interface Builder中,为UITableView创build一个原型单元。
  4. 在该原型单元格的属性表中,将UICollectionViewCell子类标识为类。
  5. 为原型单元格提供属性表中的标识符(重用标识符)。
  6. 将界面生成器中的视图子添加到原型单元格(这里是一个UIImageView)。
  7. 使用IB将IBOutlet属性映射到添加的UIImageView
  8. 然后,在cellForRowAtIndexPath中出列,将出列结果强制转换为子类(PFImageCell)并设置IBOutlet属性实例的值。 在这里,你会为你的UIImageView加载适当的图像。

UICollectionView将重用单元格以获得最大效率。 它不保证任何特定的重用或人口策略。 有趣的是,它似乎根据两个区域的整数幂来放置和移除单元格 – 例如,在非视网膜iPad上,它可能会将您的滚动区域分割成1024×1024的区域,然后在每个区域进入和退出时填充和减less这些区域的可见区域。 然而,你不应该对其确切的行为做任何预测。

另外,您使用集合视图单元格是不正确的。 请参阅文档 。 一个单元显式至less有两个子视图 – backgroundViewcontentView 。 所以,如果你添加一个子视图,它将在绝对最less的索引2,实际上,索引将是未定义的。 无论如何,你应该将子视图添加到contentView ,而不是单元格本身。

你正在做的最正常的方式是创build一个自定义的UICollectionView子类,它内部有一个PFImageView

我不确定电池是否被重复使用。 它可能被重用,但子视图可能不在那里。 我的build议是创build一个PFImageViewCollectionViewCell类(UICollectionViewCell的子类),并将其注册为CollectionView单元格,并尝试。 这就是我如何做,如果我需要一个单元格内的子视图。

尝试在这个特定的UIImageView上添加一个标签

 - (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath { static int photoViewTag = 54353532; UICollectionViewCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"FlickrCell" forIndexPath:indexPath]; cell.backgroundColor = [self generateRandomUIColor]; PFImageView *photoView = [cell.contentView viewWithTag:photoViewTag]; // Create a view // if (!photoView) { photoView = [[PFImageView alloc] initWithFrame:CGRectMake(0, 0, cell.frame.size.height, cell.frame.size.width) andImageURL:staticPhotoURL andOwningCell:cell]; imageView.tag = photoViewTag; [cell.contentView addSubview:imageView]; } // Update the current view // else { NSURL *staticPhotoURL = [self.context photoSourceURLFromDictionary:[self.photos objectAtIndex:indexPath.row] size:OFFlickrSmallSize]; photoView.imageURL = staticPhotoURL; } return cell; } 

我真的build议创build自己的UICollectionViewCell子类。

编辑:另外,请注意,我使用contentView属性,而不是直接将其添加到单元格。