背景:
最近有个需求:根据图片url链接是否合法,比如链接贴错了,多了或者少了一些字符等情况,就会导致图片加载失败,遇到这种情况,就需要隐藏掉cell里的UIImageView。
先看看这个Cell长得怎样:红框部分就是这个cell要显示的内容,1个红点UIView + 2个UILabel + 1个UIImageView。(考虑到有个红点不方便对齐,所以没有使用UIStackView)
实现思路:
-
在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
是否有效,再改变dateLabelBottomConstraint
、photoViewBottomConstraint
、isHidden
,从而决定图片是否显示。-
先使用
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() }