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

作者: Fsn_soul | 来源:发表于2016-04-24 13:47 被阅读3600次

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.

相关文章

网友评论

  • calary:帮了我大忙,谢谢
  • Beyond无状态:我目前还没有发现cell复用 我也是用的SDwebImage 如果发生复用 我在自定义cell里做不了控制器里的操作啊 咋弄
  • Apollo2016:很不错哦,帮我大忙:blush:
  • 小样飞奔:你好,我想请教个问题,我用的是SDWebImage这个框架,我是在自定义UITableViewCell,然后在这个cell里面加了个UIImageView,在imageView里面用SD去请求网络图片然后显示出来,图片较大请求需要一些时间。这段代码在iOS10之前都没有出现图片混乱的问题,应该是SD有处理过,但是在ios10之后会出现这个问题,请教 :flushed: 大神有相关的解决方案么? :flushed:
    lee_moons:@小样飞奔 我也是这个问题 ,请问你是怎么解决的
    Fsn_soul:@小样飞奔 不好意思很久没登了,我看了SDWebImage的- (void)sd_setImageWithURL:方法第一句代码就是[self sd_cancelCurrentImageLoad];cell重用后cell其实是同一个cell,cell上的imageView对象也是同一个,而SD会取消该imageView的当前的图片下载,然后再重新设置为新的图片地址并下载,因此对于重用的cell是不可能产生图片错乱的,或者说产生的错乱的概率非常低(不排除取消时当前图片恰好下载完成,并且设置的新的图片下载失败这种情况,不过概率应该很低).我自己试了一下iOS10用SD去加载也没有看到错乱的情况.所以你可以看看是不是其他原因,或者把你的问题代码发给我看下...
  • dedenc:你好,很感谢你的分享,我现在也出现这样的问题了,是用的YYWebImage这个框架。请问你还有别的解决方式吗,因为你这个判断是写在了控制器里面,如我我是在cell自己内部去实现这个问题呢,请问该如何获取到indexpath.row。 谢谢
    Fsn_soul:@dedenc 在cell这个类里定义一个indexPath属性,然后将cellForRow里的对应参数赋值给它,应该就可以了

本文标题:tableviewcell重用导致的图片错乱问题

本文链接:https://www.haomeiwen.com/subject/ovdfrttx.html