【问题标题】:How to properly load images on custom UICollectionViewCell?如何在自定义 UICollectionViewCell 上正确加载图像?
【发布时间】:2014-10-17 18:34:17
【问题描述】:

我整个下午都在这,我想不通,所以你的帮助会很棒!我有一个自定义的 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 中这是导致图像闪烁的原因吗?或者是别的什么?非常感谢您的帮助!

【问题讨论】:

    标签: ios objective-c iphone uicollectionview uicollectionviewcell


    【解决方案1】:

    有很多事情我会改变:

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

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

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

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

    5. 也许不太可能,但如果出队的单元格具有默认图像(因此 cell.imageView.image != nil)并且正在被重新用于尚未缓存图像的单元格(因此 cachedImage == nil),则不会执行任何调度方法,因此永远不会下载图像。

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

    【讨论】:

    • 别担心!我非常感谢您的反馈。我刚开始学习 Objective-C、iOS 开发和一般编程,所以这些答案很棒。我知道我做错了什么,但让它正常工作仍然取决于我。关于那个......我认为它现在正在工作!我正在对数组进行排序,当它填充到 getWatchListData 方法中时(这似乎有很大的不同)。基本上我做了你所有的建议。唯一的问题是,在第一次加载时,有时单元格会填充错误的图像。知道如何解决这个问题吗?
    • 只有几个指针:首先,检查您的缓存是否正确填充。即使您的代码现在可能没问题,它也可能在之前的运行中被错误地填充。其次,注意单元格重复使用问题,特别是当您滚动浏览列表时看到相同的图像出现时。我发现 NSLog 和断点对于追踪这类问题非常有帮助。
    【解决方案2】:

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

    就是这样。它将显示您的单元格而无需任何轻弹。谢谢。

    【讨论】:

      【解决方案3】:

      尝试使用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
                                              }
                                         }];
      

      【讨论】:

        猜你喜欢
        • 2012-10-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-05
        • 2013-05-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多