UITableViewCell图片自适应填坑之旅

本文记录之前帮助朋友开发该功能的填坑之旅,需求为tableView显示图片,图片固定宽度为屏幕宽度高度自适应,保证不失真。效果如图所示:


UITableViewCell图片自适应填坑之旅_第1张图片
图片自适应.png

乍看该需求可能并不难甚至简单的很,若API已经给予每个图片的宽高比例,那该功能很容易实现。但是对于一些初创型公司,为减少开发成本,图片的上传下载使用的可能为类似于七牛等云存储服务。图片存储在七牛服务器上,前端传递给后台的仅仅是个图片链接字符串而已。后台人员无法获取图片宽高或因为其他原因无法获取,这时就需要我们自己计算其真实显示高度。

刚开始分析需求时,想到的是通过AutoLayout进行实现,既然Cell中的Label可以实现自适应,那图片为什么不呢?

因此第一个种想法是给图片添加上下左右的周边约束,将ContentModel设置为Aspect Fill,图片加载简单使用sd_setImageWithUrl:,然后设置TableView自适应进行实现。

//行高自适应
_tableView.rowHeight = UITableViewAutomaticDimension;
//预计行高,与真实行高接近即可
_tableView.estimatedRowHeight = 44.0;

但效果却事与愿违,图片虽未失真,但Cell上却存在很多留白。只有TableView滑动重新显示时才能达到正常的状态,而且会发生动态调整缩放,简直丑到爆。显然是刷新时机不正确。之后尝试延时刷新或更新约束等方式,但是结果仍然不是很理想。最后摒弃AutoLayout,通过手动计算图片真实高度进行实现,该篇文章即由此而来。若有其他更好的方案,欢迎评论提出。

这里我们需要使用到SDWebImage中SDWebImageDownloaderSDImageCache这两个类来实现功能,分别为图片下载类于图片缓存类。具体代码如下,假设以获取所需图片网址数组为_picArr。

首先借助SDImageCache进行判断对应图片是否缓存。若已缓存,获取其真实需要显示高度(当前屏幕宽度乘图片比例即可),将获取到的高度存入高度字典中。若不存在缓存,使用SDWebImageDownloader进行下载,在下载结束后同样计算高度并缓存图片。SDWebImageDownloader下载图片为异步,无法保证图片按顺序下载完成,因此这里使用字典存储而非数组。字典键值对存储的即为所对应行应显示的高度。

for (NSInteger i = 0; i < _picArr.count; i++) {
        //获取图片网址
        NSString *picUrl = _picArr[i];
        //根据图片网址获取缓存
        UIImage *cachedImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:picUrl];
        if (cachedImage) {//若存在 计算图片真实需要显示高度 存入字典
            CGFloat height = cachedImage.size.height * self.view.bounds.size.width / cachedImage.size.width;
            [_rowHeightDict setObject:[NSNumber numberWithFloat:height] forKey:[NSNumber numberWithInteger:i]];
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.tableView reloadData];
            });
        } else {//不存在缓存 使用SDWebImageDownloader下载
            [[SDWebImageDownloader sharedDownloader]downloadImageWithURL:[NSURL URLWithString:picUrl] options:SDWebImageDownloaderProgressiveDownload progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                
            } completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
                if (finished) {
                    //对现在图片进行缓存
                    [[SDImageCache sharedImageCache] storeImage:image forKey:picUrl toDisk:YES];
                    CGFloat height = image.size.height * self.view.bounds.size.width / image.size.width;
                    [_rowHeightDict setObject:[NSNumber numberWithFloat:height] forKey:[NSNumber numberWithInteger:i]];
                }
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self.tableView reloadData];
                });
            }];
        }
    }

当前显示的行数需返回行高字典的键值对个数。

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _rowHeightDict.count;
}

若图片已缓存,加载缓存图片,若无缓存,显示默认placehold图片。

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MainTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ReusableID];
    UIImage *image = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:_picArr[indexPath.row]];
    if (!image) {
        cell.photoImageView.image = [UIImage imageNamed:@"placehold"];
    } else {
        cell.photoImageView.image = image;
    }
    return cell;
}

返回行高部分也会相应容易很多,若已计算返回计算好的行高,若无返回默认行高。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (_rowHeightDict[[NSNumber numberWithInteger:indexPath.row]]) {
        return [_rowHeightDict[[NSNumber numberWithInteger:indexPath.row]] floatValue];
    }
    return 200.0;
}

demo已做分离,尽可能以最简单的形式展示。

demo下载链接

UITableView异步加载图片自适应填坑之旅demo

你可能感兴趣的:(UITableViewCell图片自适应填坑之旅)