ios – 使用Autolayout在UITableView中异步加载图像

我有一个UITableView,可以在UITableView中异步加载图像.由于所有图像都是不同的高度,我根据图像的高度设置高度约束.

当我使用DataWithContentsOfUrl时,这可以正常工作,但会冻结UI.当我异步加载时,图像堆叠在一起,如下所示:

http://i.stack.imgur.com/TEUwK.png

我正在使用的代码如下:

NSURL * imageURL = [NSURL URLWithString:img];
NSURLRequest* request = [NSURLRequest requestWithURL:imageURL];
cell.imageViewHeightConstraint.constant=0;
[NSURLConnection sendAsynchronousRequest:request    queue:[NSOperationQueue mainQueue]  completionHandler:^(NSURLResponse * response,   NSData * data,  NSError * error) {
    if (!error){
        UIImage *imgz = [UIImage imageWithData:data];
        [cell.CellImg setBackgroundImage:imgz forState:UIControlStateNormal];
        [cell.CellImg sizeToFit];
        cell.imageViewHeightConstraint.constant=result.width*(imgz.size.height/imgz.size.width);
        [cell setNeedsUpdateConstraints];
    }
}];
[cell updateConstraints];

当我向上和向下滚动几次时,图像正确定位自己.

最佳答案 该问题未指定不同图像尺寸的所需行为.单元格是否应该更高,或者仅仅是单元格中的图像视图(看起来像代码中的按钮)?

但是我们应暂时搁置这个问题并解决代码中更严重的问题:它从cellForRowAtIndexPath发出无人看守的网络请求.结果,(a)用户来回滚动将产生许多冗余请求,并且(b)用户快速滚动很长时间将生成在启动它的单元格消失时满足的请求 – 作为单元格重用另一排.

为了解决(a),数据源应该缓存提取的图像,并且只请求那些尚未接收的图像.为了解决(b),完成块不应直接引用该单元.

一个简单的缓存看起来像这样:

@property(strong,nonatomic) NSMutableDictionary *images;

// initialize this when you initialize your model
self.images = [@{} mutableCopy];

// move the network code into its own method for clarity
- (void)imageWithPath:(NSString *)path completion:(void (^)(UIImage *, NSError *))completion {
    if (self.images[indexPath]) {
        return completion(self.images[indexPath], nil);
    }
    NSURL *imageURL = [NSURL URLWithString:path];
    NSURLRequest *request = [NSURLRequest requestWithURL:imageURL];
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        if (!error){
            UIImage *image = [UIImage imageWithData:data];
            self.images[indexPath] = image;
            completion(image, nil);
        } else {
            completion(nil, error);
        }
    }];
}

现在,我们通过首先检查cellForRowAtIndexPath中缓存中的图像来修复多个请求问题.

UIImage *image = self.images[indexPath];
if (image) {
    [cell.CellImg setBackgroundImage:image forState:UIControlStateNormal];
} else {
    // this is a good place for a placeholder image if you want one
    [cell.CellImg setBackgroundImage:nil forState:UIControlStateNormal];
    // presuming that 'img' is a string from your mode
    [self imageWithPath:img completion:^(UIImage *image, NSError *error) {
        // the image is ready, but don't assign it to the cell's subview
        // just reload here, so we get the right cell for the indexPath
        [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    }];
}

还要注意完成块中没有完成的操作……我们通过不引用单元格来修复单元重用.相反,知道图像现在被缓存,我们重新加载indexPath.

返回图像大小调整:大多数应用程序喜欢看到表格视图单元格随着可变高度子视图而变得更高或更短.如果是这种情况,那么您根本不应该在该子视图上放置高度约束.相反,将它的顶部和底部边缘限制在单元格的内容视图中(或将其包含在一个子视图链中,这些子视图将顶部和底部相互约束,并包含单元格的顶部和底部边缘的最顶部和最底部的子视图).然后(在iOS 5中,我认为),这将允许您的单元格改变与该子视图约束链的高度…

self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = // your best guess at the average height here
点赞