【问题标题】:Implementing tableView:cellForRowAtIndexPath: with a ALAssetRepresentation image使用 ALAssetRepresentation 图像实现 tableView:cellForRowAtIndexPath:
【发布时间】:2013-11-29 04:24:32
【问题描述】:

这是我在 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吗?

【问题讨论】:

  • 您的自定义单元格正在被重复使用 (dequeueReusableCellWithIdentifier:),因此创建的第一个单元格中的图像在重复使用时仍然存在。在自定义单元格的 prepareForReuse 方法中,将纵向图像设置为 nil。
  • 我之前尝试过prepareForReuse,它基本上会导致图像重新加载的无限循环。

标签: ios objective-c uitableview alassetslibrary


【解决方案1】:

无论是否设置,您都需要更新图像。如果还没有图像,您的代码只会设置图像。滚动时,单元格会被重复使用。因此,每个单元格都需要使用适合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;

【讨论】:

  • dispatch_async 使用EXC_BAD_ACCESS 地址0xc000000c 使我的应用程序崩溃。在完成之前如何保留单元格?
  • 当您的块返回时,该单元格可能正在显示不同的内容(或完全消失)。您需要记住您的索引路径,并在该索引路径中向表格询问当前单元格。
【解决方案2】:

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

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

下载图片后,使用键 (indexPath.row) 将其添加到字典中。 下载图片时应该重新加载单元格,只要记住在主线程上执行即可。

【讨论】:

    【解决方案3】:

    我建议使用图像缓存。假设图片缓存有以下 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-16
      相关资源
      最近更新 更多