You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CAShapeLayer无法正常移除,UICollectionView滑动后cell出现图层残留如何解决?

问题根因

  • UICollectionView采用单元格复用机制,你每次在cellForItemAt方法中调用addView接口时,都会生成一个全新的subView添加到cell.takeProgressView上,但你仅在addView中清除了当前TakeProgressView实例持有的图层,复用单元格上之前添加的旧子视图、旧图层完全没有被移除,多次滑动后同一个单元格上会叠加多层半透明进度层,就会出现你观察到的淡色残留痕迹。
  • 你每次调用cellForItemAt都会新建一个TakeProgressView实例,旧实例生成的子视图和图层不属于新实例,你在新实例addView方法中执行的agreeLayer.removeFromSuperlayer()这类操作,完全无法清除旧单元格上已经存在的视图和图层,操作无效。

修复方案

方案1:添加新视图前先清空目标容器的旧内容

修改addView方法,在添加新的subView之前,先把传入的目标容器view上的所有旧子视图全部移除:

func addView(view: UIView, withParticipants: Bool){
    // 新增这行代码:先清空目标容器上的所有旧子视图
    view.subviews.forEach { $0.removeFromSuperview() }
    
    // 原有逻辑保持不变
    disagreePath.removeAllPoints()
    agreePath.removeAllPoints()
    path.removeAllPoints()
    circlePath.removeAllPoints()
    agreeLayer.removeFromSuperlayer()
    disagreeLayer.removeFromSuperlayer()
    backgroundFill.removeFromSuperlayer()
    // ... 剩下的原有代码
}

方案2:优化单元格复用逻辑(更推荐)

把进度服务实例放到单元格内部作为属性,避免每次复用都重复创建实例,性能更优:

  1. 在TakeCollectionViewCell中新增属性,提前初始化进度服务:
class TakeCollectionViewCell: UICollectionViewCell {
    // 原有其他属性、IBOutlet保持不变
    @IBOutlet weak var takeProgressView: UIView!
    let progressService = TakeProgressView()
}
  1. 重写单元格的复用准备方法,在复用时提前清空旧内容:
override func prepareForReuse() {
    super.prepareForReuse()
    takeProgressView.subviews.forEach { $0.removeFromSuperview() }
}
  1. 修改cellForItemAt逻辑,复用单元格内部的进度服务实例:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellId, for: indexPath) as? TakeCollectionViewCell else {return UICollectionViewCell()}
let random = Double(indexPath.row) / 10
cell.progressService.progress = random
cell.progressService.addView(view: cell.takeProgressView, withParticipants: false)
return cell

内容的提问来源于stack exchange,提问作者Noah Iarrobino

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 21:36:01