在滚动期间重用UICollectionViewCells

时间:2014-02-06 21:45:53

标签: ios iphone uikit uicollectionview uicollectionviewcell

我有问题,

我有一个简单的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,然后在主线程上更新它自己的图像 - 这很好。

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

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

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

相反,我所看到的是UICollectionView顶部和底部的单元格已经关闭屏幕'暂时 - 当他们回到屏幕上时,他们没有被重复使用,似乎创建了一个新的单元格并且图片刷新了。

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

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

非常感谢你的时间。

约翰

4 个答案:

答案 0 :(得分:2)

UICollectionView将重复使用单元格以获得最高效率。它不保证任何特定的重用或人口策略。有趣的是,似乎基于两个区域的整数幂来放置和移除单元格 - 例如在非视网膜iPad上,它可能会将您的滚动区域划分为1024x1024的区域,然后在它们过渡进出可见区域时填充和减少每个区域。但是,你不应该预测对其确切行为的任何期望。

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

执行您正在执行的操作的最常用方法是创建一个自定义UICollectionView子类,其中包含PFImageView

答案 1 :(得分:1)

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

答案 2 :(得分:1)

我看到了几个潜在的问题:

  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的情况下设置PFImageView的URL。将可重用视图出列的想法是,您只需构建所需视图的一小部分,UITableView将在屏幕外移动时对其进行回收。您需要重置所请求的indexPath的值,即使您没有构造新内容也是如此。

  4. 如果您的案例与您描述的一样简单,您可以将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。

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

答案 3 :(得分:1)

尝试在此特定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属性,而不是直接将其添加到单元格中。