异步加载图像,单元格中的图像错误

Loading images asynchronously, wrong image in cell

我有一个 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;
}

解决此问题的一种方法是在您返回主队列后再次重新查询单元格的集合视图。此代码应该有效:

- (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;
}

我做了一个小'tutorial'来回答一个this question。尽管问题涉及核心数据,但我的回答适用于任何数据源,因此您应该能够根据您的用例进行调整。

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

(a) 还需要图片吗?

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

(b) 单元格是图像的正确单元格吗?

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

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

我最后做的是子类化 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");
        }
    }];
}  

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