SDWebImage在Cell里的使用,回调后刷新tableView并防止死循环

1,741 阅读2分钟

背景:

最近有个需求:根据图片url链接是否合法,比如链接贴错了,多了或者少了一些字符等情况,就会导致图片加载失败,遇到这种情况,就需要隐藏掉cell里的UIImageView。

先看看这个Cell长得怎样:红框部分就是这个cell要显示的内容,1个红点UIView + 2个UILabel + 1个UIImageView。(考虑到有个红点不方便对齐,所以没有使用UIStackView)

image.png

实现思路:

  • 在cell的init方法里,默认让dateLabel底部距离父视图16的间距,而让photoView底部距离父视图16的间距约束失效:

    dateLabel.snp.makeConstraints {
        ...
        dateLabelBottomConstraint = $0.bottom.equalToSuperview().inset(16).constraint
    }
    
    photoView.snp.makeConstraints {
        ...
        photoViewBottomConstraint = $0.bottom.equalToSuperview().inset(16).constraint
    }
    
    photoViewBottomConstraint?.deactivate()
    
  • 在cell的setupPhotoViewImage()方法里,传递图片URL,根据加载图片结果,判断URL是否有效,再改变dateLabelBottomConstraintphotoViewBottomConstraintisHidden,从而决定图片是否显示。

    • 先使用SDImageCache.shared.imageFromCache(forKey:)去拿SDWebImage缓存的image,如果拿到了,就直接让photoView显示image。

    • 如果没拿到缓存,则用sd_setImage去请求图片,在请求完成的回调闭包里面,判断image如果不为nil,则使用代理让外部view controller刷新当前的cell。(SDWebImage在此步会自动缓存图片,无需手动再缓存)

    • 上述2小步,就避免了刷新cell的时候又再会去执行sd_setImage,防止请求图片完成又刷新,导致死循环。

    func setupPhotoViewImage(inboxMediaURL: URL?) {
        guard let cacheImage = SDImageCache.shared.imageFromCache(forKey: inboxMediaURL.absoluteString) else {
            photoView.sd_setImage(with: inboxMediaURL) {
                [weak self] image, _, _, _ in
                guard let self = self, image != nil else { return }
                self.delegate?.reloadCellData(self)
            }
            return
        }
        photoView.image = cacheImage
        showImage()
    }
    
    private func showImage() {
        photoView.isHidden = false
        dateLabelBottomConstraint?.deactivate()
        photoViewBottomConstraint?.activate()
    }
    
  • 设置prepareForReuse(),默认停止图片请求,并隐藏图片,防止连续滚动的时候产生布局问题。

    这里使用了sd_cancelCurrentImageLoad()是为了防止cell复用的过程中请求图片发生问题。因为cell的setupPhotoViewImage方法里先去拿cache,没有拿到才会调用sd_setImage,而 sd_setImage 源码里是有调用了sd_cancelCurrentImageLoad()的,滑动屏幕,cell复用的时候,有些cell有可能不会去调用sd_setImage的,这样就没有停止加载cell里的图片,而这时之前的图片加载完成,就会将图片显示到后续复用了的cell里。

    override func prepareForReuse() {
        super.prepareForReuse()
        photoView.sd_cancelCurrentImageLoad()
        hideImage() 
    }
    
    private func hideImage() {
        photoView.isHidden = true
        photoViewBottomConstraint?.deactivate()
        dateLabelBottomConstraint?.activate()
    }