我正在尝试制作一个秒表,例如,它将计时到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)
}
}
答案 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
,它会定期“滴答”,并允许代码检查闹钟的当前状态。
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,但是此处的目的是将关注点分离开来,并且这意味着您不必在系统中不必要地添加另一个线程(不是说您不能这样做,但这只是我不想添加的一个更高级别的复杂性;))