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

drawRect重绘问题求助:返回页面后圆形计时器未按初始状态重绘

我来帮你搞定这个问题!

问题根源分析

当你从其他页面返回时,这个CountDownTimer视图的绘制状态没有被正确重置——要么是存储进度的变量保留了上次结束时的toValue值,要么是视图重新显示时没有触发初始状态的重绘,导致drawRect用了残留的状态来绘制。

具体解决方案

下面是几种针对性的处理方式,你可以根据自己的代码结构选择:

1. 在视图生命周期中重置状态并触发重绘

给CountDownTimer类添加视图生命周期方法,确保每次视图被添加到父视图(回到当前页面时)都重置进度并重新绘制:

class CountDownTimer: UIView {
    // 假设你有这些状态变量
    public var fromValue: CGFloat = 0.0
    public var toValue: CGFloat = 1.0
    private var currentProgress: CGFloat = 0.0
    
    override func didMoveToSuperview() {
        super.didMoveToSuperview()
        // 当视图被添加到父视图时(返回页面时会触发),重置进度
        if superview != nil {
            currentProgress = fromValue
            setNeedsDisplay() // 强制触发drawRect重绘
        }
    }
    
    override func draw(_ rect: CGRect) {
        // 你的绘制逻辑,必须完全依赖currentProgress变量
        // 示例绘制代码:
        guard let context = UIGraphicsGetCurrentContext() else { return }
        let center = CGPoint(x: rect.midX, y: rect.midY)
        let radius = min(rect.width, rect.height)/2 - 5
        
        // 绘制背景圆
        let bgPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
        UIColor.lightGray.setStroke()
        bgPath.lineWidth = 3
        bgPath.stroke()
        
        // 绘制进度圆
        let progressAngle = 2 * .pi * currentProgress
        let progressPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: -.pi/2, endAngle: -.pi/2 + progressAngle, clockwise: true)
        UIColor.red.setStroke()
        progressPath.lineWidth = 3
        progressPath.stroke()
    }
}

2. 在ViewController层面控制重置

如果你的CountDownTimer是某个ViewController的子视图,也可以在ViewController的viewDidAppear方法里手动重置:

class YourViewController: UIViewController {
    @IBOutlet weak var countDownTimer: CountDownTimer!
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // 重置进度并触发重绘
        countDownTimer.currentProgress = countDownTimer.fromValue
        countDownTimer.setNeedsDisplay()
        // 如果有计时器逻辑,这里也要重启计时器
        // countDownTimer.startTimer()
    }
}

3. 管理计时器的生命周期(如果用了计时器)

如果你的倒计时是用CADisplayLink或Timer实现的,一定要在视图消失时停止计时器,显示时重启并重置进度:

class CountDownTimer: UIView {
    private var displayLink: CADisplayLink?
    public var fromValue: CGFloat = 0.0
    public var toValue: CGFloat = 1.0
    private var currentProgress: CGFloat = 0.0
    
    func startTimer() {
        stopTimer()
        currentProgress = fromValue // 重置初始值
        displayLink = CADisplayLink(target: self, selector: #selector(updateProgress))
        displayLink?.add(to: .current, forMode: .common)
    }
    
    func stopTimer() {
        displayLink?.invalidate()
        displayLink = nil
    }
    
    @objc private func updateProgress() {
        // 示例进度更新逻辑,根据你的需求调整
        currentProgress += 0.01
        if currentProgress >= toValue {
            stopTimer()
        }
        setNeedsDisplay()
    }
    
    override func didMoveToSuperview() {
        super.didMoveToSuperview()
        if superview != nil {
            startTimer() // 视图显示时启动计时器
        } else {
            stopTimer() // 视图被移除时停止计时器
        }
    }
}

核心要点

  • 确保所有和绘制相关的状态变量(比如currentProgress)在视图每次显示时都被重置为fromValue
  • 每次重置状态后必须调用setNeedsDisplay()触发drawRect重绘
  • 如果有计时器逻辑,一定要在视图消失时停止,避免后台继续运行导致状态异常

内容的提问来源于stack exchange,提问作者FS.O6

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:05:10