2

我正在使用最新的 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);
                               }
                           }];
}

但它似乎不起作用,因为我不处理我正在为其加载图像的单元格是否仍然可见。

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

我找到了这篇文章,但我不知道如何实现它。

4

1 回答 1

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类别通常会取消对已重用单元格的旧请求,从而确保 UI 更快地呈现用户实际正在查看的图像。

于 2013-10-23T14:54:29.187 回答