在 Swift 中以定义的时间停止计时器 [英] Stopping timer at defined amount of time in Swift

查看:55
本文介绍了在 Swift 中以定义的时间停止计时器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to build a stopwatch which, for instance, will count to 3.0 seconds, stop, and then allow me to override the app's view with a new background/label. My issue is I cannot find a way for the timer to stop and pause on its own at 3 seconds - whenever I'd write the statement, it'd just continue counting and not do anything. Where would I put the statement in this code, and how would I write it?

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)
    }
}

推荐答案

从解耦您的期望开始.

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

A "clock" is a container for the period of time from which it was started to now. Additionally, it could be "restarted", so it may need to know how long each previous run cycle was, this would then be added into the overall duration of the "clock"

Timer 只是一种在期刊基础上运行一些代码的方法.因为 Timer 只保证至少"期间,应避免将其用于简单的计数器加法,因为它会导致您的计算出现漂移(对于简单的时钟,这可能没什么大不了的,但如果您需要任何精度,最好避免使用它)

SimpleClock

The Timer is simply a way to run some code on periodical bases. Because a Timer only guarantees "at least" period, it should avoid been used for simple counter addition, as it can cause drift in your calculations (for a simple clock, it's probably not a big deal, but if you need any kind of precision, it's best to avoid it)

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
    }
    
}

好的,这是非常基本的概念.它只是一个容器,用于记录循环"何时发生.开始和停止并管理整体"持续时间(开始/暂停/恢复周期)

Okay, this is pretty basic concept. It's just a container for recording when a "cycle" starts and stops and managing the "overall" duration (start/pause/resume cycles)

这一切都很好,但我们真正想要的是某种方法来确定周期是否超时".与否.

That's all fine and good, but what we really want is some way to determine if the period has "timeout" or not.

import Foundation

class AlarmClock: SimpleClock {
    
    var timeout: TimeInterval = 0
    
    var hasExpired: Bool {
        return duration >= timeout
    }
    
    var timeRemaining: TimeInterval {
        return max(timeout - duration, 0)
    }
    
}

所有这一切只是添加了一个超时"的概念.period 并提供一些额外的功能,让用户可以轻松确定时钟是否已过期以及剩余时间

All this does is add a concept of a "timeout" period and provides some additional functionality that allows use to easily determine if the clock has expired and the amount of time remaining

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

Okay, that's all nice a good, but how does this work (and help us)

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

Okay, this is a really simple example. It has a label and two buttons. One button starts/pauses the clock and the other resets it.

标签显示闹钟的运行时间和剩余时间.如果他的时钟到期,它会自动重置.

The label displays both the running time and the remaining time of the alarm clock. If he clock expires, it will automatically be reset.

该类包含一个 Timer,它周期性地滴答";并允许代码检查闹钟的当前状态.

The class contains a Timer which periodically "ticks" and allows the code to inspect that current state of the alarm clock.

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,但这里的目的是将关注点解耦,这意味着您不会不必要地向系统添加另一个线程(并不是说你做不到,但这只是我不想增加的复杂程度;))

Now, you could also add some kind of "internal" thread to periodically check the state of the clock and call a delegate which could then bee used to update the UI, but the intention here is the decoupling of the concerns, and this means you're not adding yet another thread to the system unnecessarily (not saying you couldn't do, but it's just one more level of complexity I didn't want to add ;))

这篇关于在 Swift 中以定义的时间停止计时器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆