tableviewcell图片错乱时如何解决
之前做过一个视频信息列表展示的模块,cell很简单就是左边图片,右边文字信息.当时用的SDWebImage加载图片并没有看到图片错乱的情况.但是如果是自己写的图片下载器,如果不注意处理,是会导致图片错乱的.
今天写了个Demo,验证及解决这个问题.
实验界面:cell依然是左边图片,右边文字信息.图片两张,一张大图片A(风景),一张小图片B(人物),采用异步下载,block里回调设置cell的图片.要求是偶数行的图片是风景,奇数行的图片是人物.
整个界面期望如下:
期望tableview界面.jpg
但实际可能出现bug,如下图:
bug界面.jpg
数据错乱原因分析
cell上的数据错乱显然是由于cell的重用导致的.由于图片是异步下载的,下载完成才给cell设置,但是在这个过程中用户可能会上下滑动,滑动的时候会导致cell的重用,比如第0行是设置大图片的,第11行是设置小图片的,但是用户可能滑到11行然后又滑到第1行,这时第1行因为cell的重用使用的是第11行的cell,所以第1行的block回调设置的cell和第11行的block回调设置的cell是同一个,这就是问题的关键.因为图片是异步下载的,你也不知道哪个block会先回调,如果小图片的block先回调那么这个cell的图片就被设置为小图片,如果后来大图片的block回来了,那么你会看到图片的替换过程替换成大图片,这种情况还算比较好,但如果大图片下载失败,那么这个cell的图片就没得替换了.你看到的将是小图片加大图片的文字信息.这时数据就错乱了.
如何解决
如果不重用cell,当然是可以解决该问题的,但是内存肯定会浪费不少.
问题的原因已经找到是因为cell的重用导致两个block回调时设置的其实是同一个cell.所以问题就变成如何在block里区分各自的cell.区分的话也只能通过indexPath来区分,block里截获的indexPath对象是以前的cell的indexPath.而通过[tableView indexPathForCell:cell];
则可以获得当前显示的cell的indexPath.如果cell发生了重用的话,那么indexPath将不一致.分析就到此结束.
另外在下载图片之前先把cell的imageView的image置为nil.cell.imgView.image = nil;
可以防止重用的cell万一图片下载失败而导致显示了以前的图片.
在block里面区分cell.具体代码如下:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
cell.imgView.image = nil;
NSString *urlStr = self.urlArr[indexPath.row];
[[XQImageDownloader defaultImageDownloader] downloadImageWithUrlString:urlStr completion:^(NSString *originalUrlString,UIImage *image, NSError *error) {
if (!error)
{
dispatch_async(dispatch_get_main_queue(), ^{
NSIndexPath *currentIndexPath = [tableView indexPathForCell:cell]; //获取cell真正的indexPath.
NSInteger currentRow = currentIndexPath.row;
NSInteger originalRow = indexPath.row; //这里的indexPath是block截获的.截获的是当时cell
if (originalRow != currentRow)
{
NSLog(@"你这个图片和当前要设置的cell图片不一致,你设置的是第%ld个cell,但现在要设置是%ld个cell", (long)originalRow, currentRow);
return ;
}
cell.imgView.image = image;
});
}
else
{
NSLog(@"error:这是第%ld个cell,%@",indexPath.row, [error localizedDescription]);
}
}];
cell.contentLabel.text = [NSString stringWithFormat:@"这是第%ld个cell:%p", indexPath.row, cell];
return cell;
}
到此就结束了.
以上.MADE BY XQ.
网友评论