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

Swift 4.1自定义TableViewCell复用时代理视图重复显示问题

解决Swift 4.1中TableViewCell复用导致CGRect重复显示的问题

嘿,这个问题我太熟悉了——UITableView的单元格复用机制就是罪魁祸首!当cell被滚出可视区域后,会被放进复用池,下次再被取出来时,你之前在addBlock里绘制的CGRect并没有被清除,所以新的数据来了之后,旧的绘制内容还留在上面,就出现了重复显示的情况。

下面给你两种最常用的解决思路,结合你的代码场景来调整:

方法一:在添加新内容前清除旧的绘制残留

如果你是通过添加子视图(比如用UIView模拟CGRect)实现的绘制,建议专门用一个容器视图管理所有绘制元素,方便批量清除:

class CustomTableViewCell: UITableViewCell {
    // 专门存放CGRect视图的容器,方便统一清除
    private let blockContainer = UIView()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        contentView.addSubview(blockContainer)
        // 设置容器约束,适配你的cell布局
        blockContainer.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            blockContainer.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
            blockContainer.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            blockContainer.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            blockContainer.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8)
        ])
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func addBlock(item: YourItemModel) {
        // 第一步:清除容器内所有旧的CGRect视图
        blockContainer.subviews.forEach { $0.removeFromSuperview() }

        // 第二步:根据当前item重新创建并添加新的CGRect视图
        let blockFrame = CGRect(x: 0, y: 0, width: item.blockWidth, height: item.blockHeight)
        let newBlockView = UIView(frame: blockFrame)
        newBlockView.backgroundColor = UIColor.systemBlue // 替换成你的样式
        blockContainer.addSubview(newBlockView)
    }
}

如果你的CGRect是通过重写draw(_ rect:)直接绘制的,要在绘制前先清除上下文:

override func draw(_ rect: CGRect) {
    super.draw(rect)
    guard let context = UIGraphicsGetCurrentContext() else { return }
    
    // 先清除当前cell的绘制上下文
    context.clear(rect)
    
    // 再根据当前数据绘制新的CGRect
    let targetRect = CGRect(x: 16, y: 8, width: 100, height: 40)
    UIColor.systemRed.setFill()
    UIBezierPath(rect: targetRect).fill()
}

方法二:利用prepareForReuse方法重置cell状态

UITableView在复用cell前会自动调用prepareForReuse,你可以在这里统一重置cell状态,清除旧绘制内容:

class CustomTableViewCell: UITableViewCell {
    private let blockContainer = UIView()

    // ... 其他初始化代码 ...

    override func prepareForReuse() {
        super.prepareForReuse()
        // 清除旧的CGRect视图
        blockContainer.subviews.forEach { $0.removeFromSuperview() }
        // 如果是draw方式,触发重绘
        self.setNeedsDisplay()
    }

    func addBlock(item: YourItemModel) {
        // 直接添加新内容即可,旧内容已经在prepareForReuse里清除了
        let blockFrame = CGRect(x: 0, y: 0, width: item.blockWidth, height: item.blockHeight)
        let newBlockView = UIView(frame: blockFrame)
        newBlockView.backgroundColor = UIColor.systemBlue
        blockContainer.addSubview(newBlockView)
    }
}

核心思路总结

不管用哪种方法,核心都是在cell复用前彻底清除之前的绘制残留,保证每次复用cell时,都是从干净的状态开始渲染当前行的数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:10:25