使用ALAssetRepresentation映像实现tableView:cellForRowAtIndexPath:

时间:2013-11-28 16:57:47

标签: ios objective-c uitableview alassetslibrary

这是我在UITableViewDataSource视图控制器

中的方法
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"studentCell";

    StudentTableCell *cell = (StudentTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    if (cell == nil) {
        // Never gets called
    }

    Student *student = self.studentList[indexPath.row];

    cell.nameFirst.text = student.nameFirst;
    cell.nameLast.text = student.portrait.assetURL;

    // Portrait
    CGImageRef portraitRef = [cell.portrait.image CGImage];
    CIImage *portraitImage = [cell.portrait.image CIImage];
    if (portraitRef == NULL && portraitImage == nil) {
        ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

        [library assetForURL:[NSURL URLWithString:student.portrait.assetURL] resultBlock:^(ALAsset *asset) {
            ALAssetRepresentation *representation = [asset defaultRepresentation];
            CGImageRef assetRef = [representation fullResolutionImage];
            if (assetRef) {
                [cell.portrait setImage:[UIImage imageWithCGImage:assetRef]];
            }
        } failureBlock:^(NSError *error) {}];
    }

    return cell;
}

这适用于适合表格初始滚动位置的前几行。

但是当我向下滚动时,cell.nameFirst.text会按预期更改,而cell.portrait.image会被回收并开始重复在第一个滚动位置内加载的图片。

问题

  1. 如何确保每个cell都有适当的图片
  2. 每个cell可以nil吗?

3 个答案:

答案 0 :(得分:2)

无论是否设置,您都需要更新图像。您的代码仅在没有图像的情况下设置图像。滚动时,单元格会被重用。因此,每个单元格都需要使用适合indexPath的图像进行更新。

另请注意assetForURL:resultBlock:failureBlock:。这是异步的。这意味着一旦获得resultBlock中的图像,就需要更新主线程上的单元格。

cell.nameFirst.text = student.nameFirst;
cell.nameLast.text = student.portrait.assetURL;

// Portrait
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

[library assetForURL:[NSURL URLWithString:student.portrait.assetURL] resultBlock:^(ALAsset *asset) {
    ALAssetRepresentation *representation = [asset defaultRepresentation];
    CGImageRef assetRef = [representation fullResolutionImage];
    if (assetRef) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [cell.portrait setImage:[UIImage imageWithCGImage:assetRef]];
        });
    }
} failureBlock:^(NSError *error) {}];

return cell;

答案 1 :(得分:1)

确保每个单元格都具有适当图像的最佳方法是创建字典,并在cellForRowAtIndexPath中:在键的字典对象中检查值(图像)(我喜欢使用indexPath.row作为键)。如果没有调用它,如果它被设置为单元格:

[library assetForURL:[NSURL URLWithString:student.portrait.assetURL] resultBlock:^(ALAsset *asset) {...

并且下载图像后,使用密钥(indexPath.row)将其添加到字典中。 您应该在下载图像时重新加载单元格,只需记住在主线程上执行此操作。

答案 2 :(得分:1)

我建议使用图像缓存。假设图像缓存具有以下API:

typedef void (^completion_t)(id result, NSError* error);

@interface SimpleImageCache : NSObject

/**
 Returns the image for the specified URL if it exists, otherwise nil.
 */
- (UIImage*) imageWithURL:(NSURL*)url;

/**
 Asychronounsly loads the image from the asset library. The compeltion handler will
 be called when the image is available or when an error occured.

 The execution context where the compeltion handler will be executed is 
 implementation defined.
 */
- (void) loadImageWithURL:(NSURL*)url completion:(completion_t)completionHandler;

@end

在您的代码中,您将按如下方式使用它:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"studentCell";
    StudentTableCell *cell = (StudentTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (cell == nil) {
        // Never gets called
    }
    Student *student = self.studentList[indexPath.row];
    cell.nameFirst.text = student.nameFirst;
    cell.nameLast.text = student.portrait.assetURL;
    // Portrait

    NSURL* url = [NSURL URLWithString:student.portrait.assetURL];
    UIImage* portrait = [self.imageCache imageWithURL:url];
    if (portrait == nil) {
        portrait = self.placeholderImage;
        [self.imageCache loadImageWithURL:url completion:^(id image, NSError*error){
            dispatch_async(dispatch_get_main_queue(), ^{
                StudentTableCell* cell = (StudentTableCell *)[tableView cellForRowAtIndexPath:indexPath];
                [cell.portrait setImage:image];
            });
        }];
    }
    [cell.portrait setImage:portrait];    
    return cell;
}

SimpleImageCache

的实施

警告:它没有经过测试,但它可能会给您一个快速启动或想法。

@interface SimpleImageCache ()

@property (nonatomic, strong) NSMutableDictionary* images;
@property (nonatomic, strong) ALAssetsLibrary* assetsLibrary;
@property (nonatomic, strong) UIImage* missingImage;
@end

@implementation SimpleImageCache {
    dispatch_queue_t _sync_queue;
}

- (id)init {
    self = [super init];
    if (self) {
        _sync_queue = dispatch_queue_create("sync_queue", NULL);
    }
    return self;
}

- (NSMutableDictionary*) images {
    if (_images == nil) {
        _images = [[NSMutableDictionary alloc] init];
    }
    return _images;
}

- (ALAssetsLibrary*) assetsLibrary {
    if (_assetsLibrary == nil) {
        _assetsLibrary = [[ALAssetsLibrary alloc] init];
    }
    return _assetsLibrary;
}

- (UIImage*) imageWithURL:(NSURL*)url {
    __block UIImage* image;
    dispatch_sync(_sync_queue, ^{
        id obj = self.images[url];
        if ([obj isKindOfClass:[UIImage class]]) {
            image = obj;
        }
    });
    return image;
}

- (void) loadImageWithURL:(NSURL*)url completion:(completion_t)completionHandler {
    dispatch_async(_sync_queue, ^{
        if (self.images[url] != nil) {
            return;
        }
        self.images[url] = @"pending";
        [self.assetsLibrary assetForURL:url resultBlock:^(ALAsset *asset) {
            ALAssetRepresentation* representation = [asset defaultRepresentation];
            __block  UIImage* image = CFBridgingRelease([representation fullResolutionImage]);
            dispatch_async(_sync_queue, ^{
                if (image == NULL) {
                    image = self.missingImage;
                    NSAssert(image, @"image is nil");
                }
                self.images[url] = image;
                if (completionHandler) {
                    dispatch_async(dispatch_get_global_queue(0, 0), ^{
                        completionHandler(image, nil);
                    });
                }
            });
        } failureBlock:^(NSError *error) {
            if (completionHandler) {
                dispatch_async(dispatch_get_global_queue(0, 0), ^{
                    completionHandler(nil, error);
                });
            }
        }];

    });
}

@end