ios - 使用自动布局在 UITableView 中异步加载图像

标签 ios objective-c uitableview asynchronous autolayout

我有一个 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];

当我上下滚动几次时,图像会正确定位。

最佳答案

问题没有指定不同图像尺寸的期望行为。单元格应该变高以适应单元格,还是单元格内的 ImageView (看起来像代码中的按钮)?

但我们应该暂时搁置这个问题,着手解决代码中更严重的问题:它从 cellForRowAtIndexPath 发出一个不 protected 网络请求。结果,(a) 用户来回滚动会生成许多冗余请求,并且 (b) 用户快速滚动很长一段路会生成一个请求,当启动它的单元格消失时,该请求得到满足 - 作为单元格重用对于另一行。

为了解决 (a),数据源应该缓存获取的图像,并且只请求那些还没有收到的图像。为了解决 (b),完成 block 不应直接引用单元格。

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

@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];
    }];
}

还要注意在完成 block 中没有做什么...我们通过不引用单元来修复单元重用。相反,我们知道图像现在已缓存,我们重新加载 indexPath。

回到图像大小:大多数应用程序喜欢看到表格 View 单元格随着可变高度 subview 变高或变短。如果是这种情况,那么您根本不应该对该 subview 施加高度限制。相反,将它的顶部和底部边缘约束到单元格的内容 View (或者将它包含在 subview 链中,这些 subview 相互约束顶部和底部并包含最顶部和最底部的 subview 顶部和底部边缘到单元格)。然后(我认为在 iOS 5+ 中),这将允许您的单元格使用该 subview 约束链更改高度...

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

关于ios - 使用自动布局在 UITableView 中异步加载图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32036290/

相关文章:

ios - 在 Swift、iOS 中将表格 View 转换为多页 PDF 的最佳方法之一

ios - xcrun 无法复制到/var/文件夹

ios - 一段时间后 dispatch_queue_t 变慢

objective-c - Objective-C : how to check if variable is NSArray or NSMutableArray

ios - 在 Evernote 链接笔记本中创建笔记

ios - UITableView 从 NIB 文件加载自定义单元格抛出 NSInternalInconsistencyException

ios - 现在 Swift 是开源的,我可以在不是 Mac 的计算机上编写和编译 iOS 应用程序吗?

iphone - Mapkit 绘制一个圆

iphone - 如何防止我的计时器应用程序被 iOS 终止

ios - 所选图像未按顺序显示