在Swift中按规定的时间停止计时器

时间:2018-06-25 21:53:24

标签: swift timer

我正在尝试制作一个秒表,例如,它将计时到3.0秒,停止,然后让我用新的背景/标签覆盖应用程序的视图。我的问题是我找不到计时器在3秒时自行停止和暂停的方法-每当我写该语句时,它只会继续计数而不执行任何操作。我将把该语句放在这段代码中的什么位置,以及如何编写它?

import UIKit

class ViewController: UIViewController {
    var time = 0.0
    var timer = Timer()

    @IBOutlet weak var lbl: UILabel!

    @IBAction func start(_ sender: UIButton)
    {
        timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(ViewController.action), userInfo: nil, repeats: false)
    }

    @IBAction func pause(_ sender: UIButton)
    {
        timer.invalidate()
    }

    @IBAction func reset(_ sender: UIButton)
    {
        timer.invalidate()
        time = 0.0
        lbl.text = ("0")
    }

    @objc func action()
    {
        time += 0.1
        lbl.text = String(time)
    }
}

1 个答案:

答案 0 :(得分:1)

从分离您的期望开始。

“时钟”是从开始到现在的时间范围内的容器。另外,它可以“重新启动”,因此它可能需要知道每个先前的运行周期有多长时间,然后将其添加到“时钟”的总持续时间中

Timer只是在定期基础上运行某些代码的一种方式。由于Timer仅保证“至少”周期,因此应避免将其用于简单的计数器加法,因为它可能导致计算漂移(对于简单的时钟,可能没什么大不了的,但是如果需要,任何一种精度,最好都避免)

SimpleClock

import Foundation

public class SimpleClock {

    internal var startedAt: Date? = nil
    internal var totalRunningTime: TimeInterval = 0 // Used for pause/resume

    var isRunning: Bool = false {
        didSet {
            if isRunning {
                startedAt = Date()
            } else {
                totalRunningTime += currentCycleDuration
                self.startedAt = nil
            }
        }
    }

    // This is the amount of time that this cycle has been running,
    // that is, the amount of time since the clock was started to now.
    // It does not include other cycles
    internal var currentCycleDuration: TimeInterval {
        guard let startedAt = startedAt else {
            return 0
        }
        return Date().timeIntervalSince(startedAt)
    }

    func reset() {
        isRunning = false
        totalRunningTime = 0
    }

    // This is the "total" amount of time the clock has been allowed
    // to run for, excluding periods when the clock was paused
    var duration: TimeInterval {
        return totalRunningTime + currentCycleDuration
    }

}

好的,这是非常基本的概念。它只是一个容器,用于记录“周期”开始和停止的时间以及管理“总体”持续时间(开始/暂停/恢复周期)

这一切都很好,但是我们真正想要的是一种确定时间段是否具有“超时”的方法。

AlarmClock

import Foundation

class AlarmClock: SimpleClock {

    var timeout: TimeInterval = 0

    var hasExpired: Bool {
        return duration >= timeout
    }

    var timeRemaining: TimeInterval {
        return max(timeout - duration, 0)
    }

}

所有这些操作都添加了“超时”期限的概念,并提供了一些附加功能,可用于轻松确定时钟是否已到期以及剩余时间量

示例

好的,这很好,但是这是如何工作的(并帮助我们)

好的,这是一个非常简单的例子。它有一个标签和两个按钮。一个按钮启动/暂停时钟,另一个按钮将其重置。

标签同时显示闹钟的运行时间和剩余时间。如果他的时钟过期,它将自动重置。

该类包含一个Timer,它会定期“滴答”,并允许代码检查闹钟的当前状态。

Clocky

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var durationLabel: UILabel!
    @IBOutlet weak var cycleButton: UIButton!
    @IBOutlet weak var resetButton: UIButton!

    let alarmClock: AlarmClock = {
        let clock = AlarmClock()
        clock.timeout = 10.0
        return clock
    }()

    var timer: Timer? = nil

    var durationFormatter: DateComponentsFormatter {
        let formatter = DateComponentsFormatter()
        formatter.allowedUnits = [.minute, .second]
        formatter.unitsStyle = .abbreviated
        return formatter
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func cycleClock(_ sender: Any) {
        alarmClock.isRunning = !alarmClock.isRunning
        if alarmClock.isRunning {
            timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(tick), userInfo: nil, repeats: true)
        } else {
            timer?.invalidate()
            timer = nil
        }
        updateDurationLabel()
        updateCycleButtonState()
    }

    @IBAction func restartClock(_ sender: Any) {
        timer?.invalidate()
        timer = nil

        alarmClock.reset()
        updateDurationLabel()
        updateCycleButtonState()
    }

    func updateCycleButtonState() {
        if alarmClock.isRunning {
            cycleButton.setTitle("Pause", for: [])
        } else {
            cycleButton.setTitle("Start", for: [])
        }
    }

    func updateDurationLabel() {
        durationLabel.text = "\(durationFormatter.string(from: alarmClock.duration)!)/\(durationFormatter.string(from: alarmClock.timeRemaining)!)"
    }

    @objc func tick() {
        print("click")
        updateDurationLabel()
        if alarmClock.hasExpired {
            restartClock(self)
        }
    }
}

现在,您还可以添加某种“内部”线程来定期检查时钟状态并调用一个委托,然后委托可以将其用于更新UI,但是此处的目的是将关注点分离开来,并且这意味着您不必在系统中不必要地添加另一个线程(不是说您不能这样做,但这只是我不想添加的一个更高级别的复杂性;))