如何在自定义UICollectionViewCell上正确加载图像?

时间:2014-10-17 18:34:18

标签: ios objective-c iphone uicollectionview uicollectionviewcell

我整个下午都在这,我无法理解,所以你的帮助会很棒!我有一个自定义的UICollectionViewCell,我正在填充我即时创建的图像。图像的路径存储在Parse中,查询完成后,我调用[self.collectionView reloadData].

在我的cellForItemAtIndexPath中,我有以下代码:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

MovieCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
// Configure the cell
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"original_title" ascending:YES];
[self.moviesforWatchlist sortUsingDescriptors:[NSArray arrayWithObjects:sort, nil]];
cell.userInteractionEnabled = YES;
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
[cell addSubview:spinner];

NSDictionary *movie = self.moviesforWatchlist[indexPath.row];
NSString *moviePosterPath = movie[@"poster_path"];
NSString *posterURL = [NSString stringWithFormat:@"http://image.tmdb.org/t/p/w342%@", moviePosterPath];
NSURL *posterImage = [NSURL URLWithString:posterURL];
UIImage *cachedImage = [self.imageCache objectForKey:posterImage];

if (cachedImage) {
    cell.imageView.image = cachedImage;
}
else if (cell.imageView.image == nil) {
    cell.imageView.image = [UIImage imageNamed:@"default_collectionviewcell.jpeg"];
    dispatch_queue_t downloadQueue = dispatch_queue_create("get image data", NULL);
    dispatch_async(downloadQueue, ^{
        [spinner startAnimating];
        NSData *imageData = [NSData dataWithContentsOfURL:posterImage];
        UIImage *image = [UIImage imageWithData:imageData];
        dispatch_async(dispatch_get_main_queue(), ^{
            [spinner stopAnimating];
            MovieCollectionViewCell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];
            if (updateCell) {
                updateCell.imageView.image = image;
                [updateCell reloadInputViews];
                [self.collectionView reloadItemsAtIndexPaths:[self.collectionView indexPathsForVisibleItems]];
         }
        });
        [self.imageCache setObject:image forKey:posterImage];
    });
    }
[self.collectionView reloadItemsAtIndexPaths:[self.collectionView indexPathsForVisibleItems]];
return cell;
}

我正在调用这样的查询方法:

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self getWatchlistData];
    [self.collectionView reloadData];
} 

这会导致图像闪烁,直到图像加载到UICollectionViewCells。我希望我的collectionviewcell中显示的图像按字母顺序排列,所以我正在对包含这样做的路径的数组进行排序,在cellforitemsatindexpath这是什么导致图像闪烁?或者是别的什么?非常感谢您的帮助!

3 个答案:

答案 0 :(得分:0)

我会改变一些事情:

  1. 我不会在cellForItemsAtIndexPAth中对您的数组进行排序。当每个单元格显示在屏幕上时调用该方法,因此当不需要时,您可能会多次使用它。如果您的数据在创建的单元格和下一个单元格之间发生变化,那么重新排序可能会破坏collectionView indexPath.row和您的数组索引之间的链接。我会在填充数组时对其进行排序,可能是在viewWillAppeargetWatchlistData方法中。如果此后数据发生了变化,请使用其他一些collectionView委托/数据源方法来处理插入/删除/移动。但我不认为这会导致图像闪烁。

  2. 在您回发到主队列的代码中,您调用cellForItemAtIndexPath方法,更新单元格图像并调用reloadInputViews(可能以某种方式重新配置单元格)。但是,然后您为所有可见单元格调用reloadItemsAtIndexPaths。这样做会导致collectionView为每个可见项重新调用cellForItemAtIndexPath数据源方法,从而导致从缓存中重新加载每个图像。我认为这是你闪烁的原因。我只是将下载的图像保存到缓存中,然后仅针对受影响的一个indexPath调用reloadItemsAtIndexPaths。将调用cellForRowAtIndexPath数据源方法,并将从缓存中恢复图像并将其添加到新单元格。

  3. 同样,我认为reloadItemsAtIndexPaths末尾的cellForItemAtIndexPath是多余的,可能会导致闪烁。

  4. 我认为您有一些细胞重用问题的风险 - 例如,您将微调器添加到每个细胞。但是出列的细胞可能已经显示过,因此已经有了一个微调器(尽管已停止,因此可能已隐藏)。如果你来回滚动很多,你最终可能会遇到带有大量微调器的单元,浪费内存。在将它作为子视图添加之前,我会给微调器一个标签,然后使用viewWithTag来确定是否已经将一个微调器添加到出队的单元格中(如果是这样,将其删除)。并且只在必要时添加微调器(即在if ...image == nil子句中)。

  5. 也许不太可能,但是如果出列的单元格有一个默认图像(所以cell.imageView.image!= nil)并且正在被重用于尚未缓存图像的单元格(所以cachedImage == nil),那么你的调度方法都不会被执行,所以永远不会下载图像。

  6. 对不起,如果那有点重,但我希望它有用。

答案 1 :(得分:0)

我有同样的问题,当任何图像下载后,它会轻弹视图。我之前要重新加载那个单元格,所以它会调用collectionview的一些委托方法。所以我删除它并为重载单元格制作单独的方法。现在当任何新图像重新加载时,我调用此方法而不是reloadCellForIndexPath:。在自定义方法中,只需从您的cellForItemAtIndexPath:复制粘贴代码。在自定义方法中分配单元格     CollectionPhotoItem cell =(CollectionPhotoItem )[self.collection cellForItemAtIndexPath:indexPath];

就是这样。它将显示你的细胞没有任何电影。感谢。

答案 2 :(得分:-1)

尝试使用DLImageLoader

[[DLImageLoader sharedInstance] displayImageFromUrl:@"image_url_here"
                                      imageView:@"UIImageView here"];

[[DLImageLoader sharedInstance] loadImageFromUrl:@"image_url_here"
                                   completed:^(NSError *error, UIImage *image) {
                                        if (error == nil) {
                                            // if we have no any errors
                                        } else {
                                            // if we got an error when load an image
                                        }
                                   }];