首页 > 解决方案 > 使用 Swift 的计时器只能倒计时 20 秒,而不是 20 分钟

问题描述

我正在我的 iOS 应用程序上创建一个计时器,它从 20 分钟倒计时到 0 秒,一次倒计时一秒。到目前为止,计时器工作但只倒计时 20 秒,而不是分钟。当它变为零时它也不会停止。如何解决?

import UIKit

class SkippingViewController: UIViewController {
    @IBOutlet weak var timeLabel: UILabel!
    @IBOutlet weak var startWorkoutButton: UIButton!
    @IBOutlet weak var pauseWorkoutButton: UIButton!

    var timer = Timer()
    var counter = 20.00
    var isRunning = false

    override func viewDidLoad() {
        super.viewDidLoad()

        timeLabel.text = "\(counter)"
        startWorkoutButton.isEnabled = true
        pauseWorkoutButton.isEnabled = false
    }

    @IBAction func startWorkoutButtonDidTouch(_ sender: Any) {
        if !isRunning {
            timer = Timer.scheduledTimer(timeInterval: -0.01, target: self, selector: #selector(SkippingViewController.updateTimer), userInfo: nil, repeats: true)

            startWorkoutButton.isEnabled = false
            pauseWorkoutButton.isEnabled = true
            isRunning = true
        }
    }

    @IBAction func pauseWorkoutButtonDidTouch(_ sender: Any) {
        startWorkoutButton.isEnabled = true
        pauseWorkoutButton.isEnabled = false

        timer.invalidate()
        isRunning = false
    }

    @objc func updateTimer() {
        counter -= 0.01
        timeLabel.text = String(format: "%.01f", counter)
    }

标签: iosswiftnstimer

解决方案


我在您的代码中看到了一些问题,这些问题阻止了它按照您想要的方式工作。

首先,如果timeInterval传入的scheduledTimer方法是负数,它总是会导致创建一个每 0.1 毫秒触发一次的计时器(来源:文档)。

您想要的是updateTimer每秒被调用一次,因此只需将 1.0 传递给该scheduledTimer方法,例如:

timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(SkippingViewController.updateTimer), userInfo: nil, repeats: true)

此外,您希望在启动计时器后 20 分钟后使此计时器无效。所以在设置定时器时,你可以跟踪当前时间,当那个时间+20分钟比调用时的当前时间多时updateTimer,你可以使定时器失效。换句话说,我们不是从 20 分钟倒数,而是从 0 到 20 分钟过去了!

示例代码(没有尝试编译它,但应该可以工作,如果没有,请告诉我):

import UIKit

class SkippingViewController: UIViewController {
    @IBOutlet weak var timeLabel: UILabel!
    @IBOutlet weak var startWorkoutButton: UIButton!
    @IBOutlet weak var pauseWorkoutButton: UIButton!

    var timer = Timer()
    var countDownFromMinutes = 20
    var timerStartTime: Date?

    override func viewDidLoad() {
        super.viewDidLoad()

        timeLabel.text = "\(countDownFromMinutes):00"
        startWorkoutButton.isEnabled = true
        pauseWorkoutButton.isEnabled = false
    }

    @IBAction func startWorkoutButtonDidTouch(_ sender: Any) {
        if !timer.isValid {
            // run every second
            timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(SkippingViewController.updateTimer), userInfo: nil, repeats: true)
            timerStartTime = nil
            startWorkoutButton.isEnabled = false
            pauseWorkoutButton.isEnabled = true
        }
    }

    @IBAction func pauseWorkoutButtonDidTouch(_ sender: Any) {
        startWorkoutButton.isEnabled = true
        pauseWorkoutButton.isEnabled = false

        timer.invalidate()
    }

    @objc func updateTimer() {
        guard let startTime = timerStartTime else {
            // first firing
            timeLabel.text = "\(countDownFromMinutes):00"
            timerStartTime = Date()
            return
        }

        let now = Date()
        let calendar = Calendar.current

        // this ordinarily never returns nil, return gracefully if so
        guard let endTime = calendar.date(byAdding: .minute, value: countDownFromMinutes, to: startTime) else {
            return
        }

        let differenceMinuteSeconds = Calendar.current.dateComponents([.minute, .second], from: now, to: endTime)

        // 20 minutes have passed since start
        if now >= endTime {
            timeLabel.text = "00:00"
            timer.invalidate()
            return
        }

        timeLabel.text = String(format: "%02d:%02d", differenceMinuteSeconds.minute ?? 0, differenceMinuteSeconds.seconds ?? 0)
    }
}

请注意,我还isRunning用 using 替换了您添加的 var,timer.isValid因为我相信它会在不引入另一个变量的情况下实现相同的效果。

唯一可能剩下的就是关于您正在显示的标签的文本。我不确定您实际上想在此处显示什么。如果您可以在评论中添加它,我可以建议一种方法。


推荐阅读