tableviewcell重用导致的图片错乱问题

tableviewcell图片错乱时如何解决

之前做过一个视频信息列表展示的模块,cell很简单就是左边图片,右边文字信息.当时用的SDWebImage加载图片并没有看到图片错乱的情况.但是如果是自己写的图片下载器,如果不注意处理,是会导致图片错乱的.

今天写了个Demo,验证及解决这个问题.
实验界面:cell依然是左边图片,右边文字信息.图片两张,一张大图片A(风景),一张小图片B(人物),采用异步下载,block里回调设置cell的图片.要求是偶数行的图片是风景,奇数行的图片是人物.
整个界面期望如下:


期望tableview界面.jpg

但实际可能出现bug,如下图:


bug界面.jpg

数据错乱原因分析

cell上的数据错乱显然是由于cell的重用导致的.由于图片是异步下载的,下载完成才给cell设置,但是在这个过程中用户可能会上下滑动,滑动的时候会导致cell的重用,比如第0行是设置大图片的,第11行是设置小图片的,但是用户可能滑到11行然后又滑到第0行,这时第0行因为cell的重用使用的是第11行的cell,所以第0行的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.

推荐阅读更多精彩内容

  • 要解决cell重用错乱的问题首先要了解重用的机制是什么,重用简单明了的来讲就是: 注册了cell后,使用了重用机制...
    健贱阅读 480评论 4 7
  • 2017.02.22 可以练习,每当这个时候,脑袋就犯困,我这脑袋真是神奇呀,一说让你做事情,你就犯困,你可不要太...
    Carden阅读 276评论 0 0
  • iOS的坐标系 原点是屏幕的左上角,各机型取值范围如下表所述: UIView 视图的类(略讲) @property...
    JavaLily阅读 2,777评论 0 25
  • 他手上握着可怜的安静 在杏树下,等一朵花开 头顶白云,嘶嘶地生长 人浮了一下午,屋顶睡那么多瓦片 他收留了一个故事...
    我是不是蝎大人阅读 39评论 0 0
  • 协方差的定义 在统计学上,协方差用来刻画两个随机变量之间的相关性,反映的是变量之间的二阶统计特性,两个随机变量Xi...
    marine0131阅读 1,015评论 0 0