当单元格不可见时,停止异步UITableViewCell图像加载?

时间:2013-10-23 14:32:30

标签: ios objective-c uitableview lazy-loading

我正在使用最新的SDK开发iOS 5.0+应用程序。

这是我用来UITableViewCell异步加载图片的代码。

- (UITableViewCell*)tableView:(UITableView *)tableView
        cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ((groups != nil) && (groups.count > 0))
    {
        GroupCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell == nil)
        {
            cell = [[GroupCell alloc] initWithStyle:UITableViewCellStyleDefault
                                     reuseIdentifier:CellIdentifier];
        }
        // Configure the cell...
        Group* group = [groups objectAtIndex:indexPath.row];

        cell.GroupNameLabel.text = group.Name;
        // TODO: Poner el estado.
        if (group.Photo)
            cell.GroupImageView.image = group.Photo;
        else
        {
            // download the photo asynchronously
            NSString *urlString =
                [NSString stringWithFormat:kGetGroupImageURL, [group.GroupId intValue]];
            NSURL *url = [NSURL URLWithString:urlString];

            [ImageTool downloadImageWithURL:url completionBlock:^(BOOL succeeded, UIImage *image) {
                if (succeeded)
                {
                    // change the image in the cell
                    cell.GroupImageView.image = image;

                    // cache the image for use later (when scrolling up)
                    group.Photo = image;
                }
            }];
        }

        return cell;
    }
    else
        return nil;
}

装载机:

#import "ImageTool.h"

@implementation ImageTool

+ (void)downloadImageWithURL:(NSURL *)url
             completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock
{
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [NSURLConnection sendAsynchronousRequest:request
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
                               if ( !error )
                               {
                                   UIImage *image = [[UIImage alloc] initWithData:data];
                                   completionBlock(YES,image);
                               } else{
                                   completionBlock(NO,nil);
                               }
                           }];
}

但它似乎不会起作用,因为如果我正在加载图像的单元格仍然可见,我就无法处理。

如果单元格仍然可见,我该如何处理?

我找到了这个article,但我不知道如何实现它。

1 个答案:

答案 0 :(得分:4)

您通常通过使用UITableView方法cellForRowAtIndexPath:检查单元格是否仍然可见来处理此问题,如果该索引路径的单元格不再可见,则返回nil。请注意,此方法不应与名称相似的UITableViewDataSource方法tableView:cellForRowAtIndexPath:混淆。

if (group.Photo)
    cell.GroupImageView.image = group.Photo;
else
{
    // don't forget to `nil` or use placeholder for `GroupImageView` because if
    // the cell is reused, you might see the old image for the other row momentarily
    // while the new image is being retrieved

    cell.GroupImageView.image = [UIImage imageNamed:@"placeholder.png"];

    // download the photo asynchronously
    NSString *urlString =
        [NSString stringWithFormat:kGetGroupImageURL, [group.GroupId intValue]];
    NSURL *url = [NSURL URLWithString:urlString];

    [ImageTool downloadImageWithURL:url completionBlock:^(BOOL succeeded, UIImage *image) {
        if (succeeded)
        {
            dispatch_async(dispatch_get_main_queue(), ^ {
                GroupCell *updateCell = [tableView cellForRowAtIndexPath:indexPath];
                if (updateCell) 
                {
                    // change the image in the cell
                    updateCell.GroupImageView.image = image;
                }

                // cache the image for use later (when scrolling up)
                group.Photo = image;
            });
        }
    }];
}

注意,如果sendAsynchronousRequest的完成块未提交到主队列(例如,您可能会做一些计算上昂贵的事情,而您不想将主队列绑定),您可以进一步调度UI更新到主队列,如上所示。您可以在此处执行此操作,如上所示,也可以在downloadImageWithURL中执行此操作。但请确保更新主队列上的UI(并避免同步问题,同时更新group)。但是,由于您已为操作队列指定了主队列,因此这不是一个关键问题。

正如我在代码注释中指出的那样,如果您正在为已经重用的单元格检索图像,请不要忘记将GroupImageView.image属性重置为nil。如果不这样做,您可能会看到重新使用的单元格的上一个图像在新行的请求正在进行时暂时显示。


或者,您应该使用UIImageView类别,例如SDWebImage提供的类别,它会为您处理所有这些(包括缓存管理)。

顺便说一下,它还处理一个微妙的问题,而你的代码却没有这个问题,即在慢速互联网连接上通过长表视图快速滚动的地方。您实现不会加载当前可见单元格的图像,直到所有其他单元格的图像都已加载(因为一次只能有五个并发NSURLConnection个对象)。这些UIImageView类别通常会取消对已重复使用的单元格的旧请求,从而确保用户界面正在更快地呈现用户实际查看的图像。