【问题标题】:Loading images asynchronously, wrong image in cell异步加载图像,单元格中的图像错误
【发布时间】:2015-07-14 23:49:59
【问题描述】:

我有一个UICollectionView,我在其中加载了多个图像。从我一直在阅读的内容来看,为了将正确的图像与每个单元格匹配,我需要将UIImageView 子类化并在那里获取图像。因为每次我collectionView reloadData时,都会有一些图像重复,而且它们都乱码了。但我不确定如何做到这一点,也没有找到任何教程。我正在使用 Parse 作为数据库。

 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
albumImageCell *cell = (albumImageCell *) [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];

     if (cell == nil) {
         cell = [[albumImageCell alloc]init];
     }

     PFObject *temp = [_dataArray objectAtIndex:indexPath.row];
     PFFile *file = [temp objectForKey:@"imageThumbnail"];

     if (![cell.hasImage isEqualToString:@"YES"]) {
         dispatch_async(imageQueue, ^{
             NSData *data = [file getData];
                 if (data) {
                     UIImage *image = [UIImage imageWithData:data];

                     dispatch_async(dispatch_get_main_queue(), ^(void){
                         cell.imageView.image = image;
                         cell.hasImage = @"YES";
                     });

                 }
         });
     }

     return cell;
}

【问题讨论】:

  • dispatch_async 返回时,您分配数据的UICollectionViewCell 可能已经被重新分配。

标签: ios objective-c asynchronous uicollectionview


【解决方案1】:

解决此问题的一种方法是在您回到主队列后再次重新查询单元格的集合视图。这段代码应该可以工作:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
albumImageCell *cell = (albumImageCell *) [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];

     if (cell == nil) {
         cell = [[albumImageCell alloc]init];
     }

     PFObject *temp = [_dataArray objectAtIndex:indexPath.row];
     PFFile *file = [temp objectForKey:@"imageThumbnail"];

     if (![cell.hasImage isEqualToString:@"YES"]) {
         dispatch_async(imageQueue, ^{
             NSData *data = [file getData];
                 if (data) {
                     UIImage *image = [UIImage imageWithData:data];

                     dispatch_async(dispatch_get_main_queue(), ^(void){
                         // cellAgain will be the actual cell at that index path, if it is visible. 
                         // If it is not visible, cellAgain will be nil.
                         albumImageCell *cellAgain = [collectionView cellForItemAtIndexPath:indexPath];
                         cellAgain.imageView.image = image;
                         cellAgain.hasImage = @"YES";
                     });

                 }
         });
     }

     return cell;
}

【讨论】:

  • 不仅需要重新查询,还需要将图片重置为nil,否则重复使用的单元格可能包含旧数据。 cell.imageView.image = nil; // Or placeholder image
  • 好电话。知道我忘记了什么。这应该放在单元格的 -prepareForReuse 或我的答案中方法的顶部。
【解决方案2】:

我针对this question 做了一个小“教程”。虽然问题是指核心数据,但我的回答适用于任何数据源,因此您应该能够适应您的用例。

当您回到主队列时,您需要注意的一件事是内部块。鉴于您不知道到达该点需要多长时间,因此该单元格可能不再与该图像相关(可能已被重复使用),因此您需要进行一些额外的检查...

(a) 还需要图片吗?

if ([[tableView indexPathsForVisibleRows] containsObject:indexPath])

(b) 那个单元格是图片的正确单元格吗?

 UITableViewCell * correctCell = [self.tableView cellForRowAtIndexPath:indexPath];

虽然本教程仍然有效,但这些天我倾向于进一步抽象事物。由于 viewController 必须处理 UIKit 和 Core Data 等线程不安全的实体,所以最好将 all viewController 代码保留在主线程上。后台队列抽象应该发生在较低级别,最好是在模型代码中。

【讨论】:

    【解决方案3】:

    我最终做的是继承UIImaveView,然后将图像文件传递给cellForRow

    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    albumImageCell *cell = (albumImageCell *) [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
    
        if (cell == nil) {
            cell = [[albumImageCell alloc]init];
        }
    
        PFObject *temp = [_dataArray objectAtIndex:indexPath.row];
        PFFile *file = [temp objectForKey:@"imageThumbnail"];
    
        [cell.imageView setFile:file];
    
        return cell;
    }  
    

    然后在customImageView -

    - (void) setFile:(PFFile *)file {
    
        NSString *requestURL = file.url; // Save copy of url locally (will not change in block)
        [self setUrl:file.url]; // Save copy of url on the instance
        self.image = nil;
        [file getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
            if (!error) {
                UIImage *image = [UIImage imageWithData:data];
                if ([requestURL isEqualToString:self.url]) {
                    [self setImage:image];
                    [self setNeedsDisplay];
                }
            } else {
                NSLog(@"Error on fetching file");
            }
        }];
    }  
    

    但是每次用户滚动到一个新单元格时,这都会获取数据。所以我仍在尝试找出如何将特定图像与单元格匹配,而不是每次都获取数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-15
      • 1970-01-01
      • 2019-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-27
      相关资源
      最近更新 更多