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

UITableViewCell复用CAShapeLayer时图层残留及遍历异常问题求助

UITableViewCell复用CAShapeLayer残留问题解决方法

核心问题原因

你之前触发异常是因为直接遍历contentView.layer.sublayers的同时删除图层,这个数组是动态的,枚举过程中修改数组结构会导致CALayerArray was mutated while being enumerated错误。下面是几种可靠的解决方式:


方案一:用属性持有单个CAShapeLayer(最简洁)

在自定义UITableViewCell中声明一个属性专门持有当前的状态图层,每次刷新时先移除旧图层再创建新的:

import UIKit

class CustomCell: UITableViewCell {
    // 持有当前的状态图层
    var statusShapeLayer: CAShapeLayer?
    
    func configure(with status: Int) {
        // 先移除旧图层
        statusShapeLayer?.removeFromSuperlayer()
        
        // 创建并配置新图层
        let newLayer = CAShapeLayer()
        // 根据status设置不同的路径、颜色等
        switch status {
        case 0:
            newLayer.path = UIBezierPath(rect: CGRect(x: 10, y: 10, width: 20, height: 20)).cgPath
            newLayer.fillColor = UIColor.red.cgColor
        case 1:
            newLayer.path = UIBezierPath(ovalIn: CGRect(x: 10, y: 10, width: 20, height: 20)).cgPath
            newLayer.fillColor = UIColor.green.cgColor
        default:
            newLayer.path = UIBezierPath(roundedRect: CGRect(x: 10, y: 10, width: 20, height: 20), cornerRadius: 4).cgPath
            newLayer.fillColor = UIColor.gray.cgColor
        }
        
        // 添加到contentView并更新持有引用
        contentView.layer.addSublayer(newLayer)
        statusShapeLayer = newLayer
    }
}

方案二:通过Tag标记图层,先收集再删除

如果需要同时管理多个同类型图层,可给图层设置固定Tag,先把要删除的图层收集到临时数组,再批量删除:

func configure(with status: Int) {
    // 定义状态图层的专属Tag
    let statusLayerTag = 1001
    // 收集所有对应Tag的旧图层
    let oldLayers = contentView.layer.sublayers?.filter { $0.tag == statusLayerTag } ?? []
    // 批量删除
    oldLayers.forEach { $0.removeFromSuperlayer() }
    
    // 创建新图层并设置Tag
    let newLayer = CAShapeLayer()
    newLayer.tag = statusLayerTag
    // 根据status配置图层...
    
    contentView.layer.addSublayer(newLayer)
}

方案三:利用prepareForReuse提前清理(更符合复用生命周期)

重写Cell的prepareForReuse方法,在Cell被复用前主动清理旧图层,避免残留:

class CustomCell: UITableViewCell {
    var statusShapeLayer: CAShapeLayer?
    
    override func prepareForReuse() {
        super.prepareForReuse()
        // 清理旧图层
        statusShapeLayer?.removeFromSuperlayer()
        statusShapeLayer = nil
        // 或者用Tag方式清理
        // contentView.layer.sublayers?.filter { $0.tag == 1001 }.forEach { $0.removeFromSuperlayer() }
    }
    
    func configure(with status: Int) {
        // 直接创建并配置新图层即可,旧图层已被prepareForReuse清理
        let newLayer = CAShapeLayer()
        // 根据status设置属性...
        
        contentView.layer.addSublayer(newLayer)
        statusShapeLayer = newLayer
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 22:22:39