在滚动期间重用 UICollectionViewCells

Posted

技术标签:

【中文标题】在滚动期间重用 UICollectionViewCells【英文标题】:Reuse of UICollectionViewCells during scrolling 【发布时间】:2014-02-06 21:45:52 【问题描述】:

我有问题,

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

我的 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,然后在主线程上更新它自己的图像 - 这工作正常。

逻辑非常简单 - 如果没有可出列的单元格,我创建一个单元格。

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

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

相反,我看到的是 UICollectionView 顶部和底部的单元格暂时“离开屏幕” - 当它们返回屏幕时,它们没有被重用,并且似乎创建了一个新单元格并刷新了图片。

1) 如何在创建单元格后获得静态图像(即当单元格稍微离开屏幕时不刷新。

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

非常感谢您的宝贵时间。

约翰

【问题讨论】:

【参考方案1】:

UICollectionView 将重复使用单元以实现最大效率。它不保证任何特定的重用或填充策略。有趣的是,它似乎基于两个区域的整数幂来放置和删除单元格——例如在非视网膜 iPad 上,它可能会将您的滚动区域划分为 1024x1024 的区域,然后在这些区域进出可见区域时填充和取消填充每个区域。但是,您不应该对它的确切行为做出任何预期。

此外,您对集合视图单元格的使用不正确。见the documentation。一个单元格至少有两个子视图——backgroundViewcontentView。因此,如果您添加一个子视图,它将至少位于索引 2 处,实际上,该索引将是未定义的。在任何情况下,您都应该将子视图添加到contentView,而不是单元格本身。

最正常的做法是创建一个自定义的 UICollectionView 子类,其中固有地包含一个 PFImageView

【讨论】:

【参考方案2】:

我发现了几个潜在问题:

    您正在专门查看要添加的子类的单元格的索引 0。 UICollectionViewCell 可能有其他视图作为子视图,因此您不能只假设唯一(或第一个)子视图是您添加的视图。

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

    您只是在必须构造 PFImageView 的情况下设置 PFImageView 的 URL。出队可重用视图的想法是,您将只构建所需视图的一小部分,并且 UITableView 将在它们移出屏幕时回收它们。您需要重置所请求的 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 e.g. 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。

    在项目中创建 UICollectionViewCell 的子类(在您的情况下,将其称为 PFImageCell)。 为我想在初始化时更改的视图(在您的情况下为 UIImageView)添加一个 IBOutlet 属性到该子类。 @property (nonatomic, assign) IBOutlet UIImageView *image; 在 Interface Builder 中,为 UITableView 创建一个原型单元格。 在该原型单元格的属性表中,将 UICollectionViewCell 子类标识为类。 在属性表中为原型单元格指定一个标识符(重用标识符)。 将界面构建器中的子视图添加到原型单元格(此处为 UIImageView)。 使用 IB 将 IBOutlet 属性映射到添加的 UIImageView 然后,在 cellForRowAtIndexPath 出队时,将出队结果强制转换为子类 (PFImageCell) 并设置 IBOutlet 属性实例的值。在这里,您将为 UIImageView 加载正确的图像。

【讨论】:

【参考方案3】:

我不确定该单元是否被重复使用。它可能正在被重用,但子视图可能不存在。我的建议是创建一个 PFImageViewCollectionViewCell 类(UICollectionViewCell 的子类)并将其注册为 CollectionView Cell 并尝试。如果我需要一个单元格内的子视图,我就是这样做的,并且会这样做。

【讨论】:

【参考方案4】:

尝试在这个特定的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;

我真的建议您创建自己的 UICollectionViewCell 子类。

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

【讨论】:

以上是关于在滚动期间重用 UICollectionViewCells的主要内容,如果未能解决你的问题,请参考以下文章

uicollectionview 可重用单元格图像在滚动时加载

TableView 单元格重用导致按钮标题在滚动时更改

[UGUI]ListLayoutGroup--可重用的滚动列表

滚动时 UITableViewCells 没有被重用

滚动后自定义 UITableViewCell 按钮标题不再可见(重用)

滚动时固定在页面顶部的菜单栏导致下面的内容在滚动期间跳转