如何制作秒表计时器
How to make a stopwatch timer
出于某种原因,秒表看起来还不错,但与实际交易相比,它走得太快了。我不明白我应该做什么。是我应该改变的时间间隔吗?我希望它显示毫秒、秒和分钟,但我尝试的一切只会让情况变得更糟。谢谢
import UIKit
class ViewController: UIViewController {
// Outlets
@IBOutlet weak var minutes: UILabel!
@IBOutlet weak var seconds: UILabel!
@IBOutlet weak var milliSecondsLabel: UILabel!
@IBOutlet weak var resetButton: UIButton!
@IBOutlet weak var startButton: UIButton!
@IBOutlet weak var pauseButton: UIButton!
// Variables
var timer = Timer()
var time: Int = 0
var running: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
roundButtons()
}
@IBAction func resetTimer(_ sender: Any) {
timer.invalidate()
time = 0
updateUI()
running = false
}
@IBAction func startTimer(_ sender: Any) {
if running {
return
} else {
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(timerDidEnd), userInfo: nil, repeats: true)
running = true
}
}
@IBAction func pauseTimer(_ sender: Any) {
timer.invalidate()
running = false
}
func roundButtons() {
resetButton.layer.cornerRadius = resetButton.frame.height / 2
startButton.layer.cornerRadius = startButton.frame.height / 2
pauseButton.layer.cornerRadius = pauseButton.frame.height / 2
}
@objc func timerDidEnd() {
time += 1
updateUI()
}
func updateUI() {
var min: Int
var sec: Int
var mil: Int
min = time / (60*60)
sec = (time/60)%60
mil = time & 60
minutes.text = String(min)
seconds.text = String(sec)
milliSecondsLabel.text = String(mil)
}
}
我建议两件事:
不要自己数时间。捕获开始时间并从中计算经过的时间。您可以使用 Date
方法 timeIntervalSince
, or, because that's not guaranteed to return monotonically increasing values, use CACurrentMediaTime
,如下所示。
与其使用每秒任意更新 100 次的计时器,不如使用 CADisplayLink
,它针对设备屏幕刷新率进行了最佳计时。
例如:
class ViewController: UIViewController {
// Outlets
@IBOutlet weak var minutesLabel: UILabel!
@IBOutlet weak var secondsLabel: UILabel!
@IBOutlet weak var milliSecondsLabel: UILabel!
@IBOutlet weak var resetButton: UIButton!
@IBOutlet weak var startButton: UIButton!
@IBOutlet weak var pauseButton: UIButton!
// Variables
private weak var displayLink: CADisplayLink?
private var startTime: CFTimeInterval?
private var elapsed: CFTimeInterval = 0
private var priorElapsed: CFTimeInterval = 0
override func viewDidLoad() {
super.viewDidLoad()
setFonts()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
roundButtons()
}
@IBAction func resetTimer(_ sender: Any) {
stopDisplayLink()
elapsed = 0
priorElapsed = 0
updateUI()
}
@IBAction func startTimer(_ sender: Any) {
if displayLink == nil {
startDisplayLink()
}
}
@IBAction func pauseTimer(_ sender: Any) {
priorElapsed += elapsed
elapsed = 0
displayLink?.invalidate()
}
}
private extension ViewController {
func startDisplayLink() {
startTime = CACurrentMediaTime()
let displayLink = CADisplayLink(target: self, selector: #selector(handleDisplayLink(_:)))
displayLink.add(to: .main, forMode: .common)
self.displayLink = displayLink
}
func stopDisplayLink() {
displayLink?.invalidate()
}
@objc func handleDisplayLink(_ displayLink: CADisplayLink) {
guard let startTime = startTime else { return }
elapsed = CACurrentMediaTime() - startTime
updateUI()
}
func updateUI() {
let totalElapsed = elapsed + priorElapsed
let hundredths = Int((totalElapsed * 100).rounded())
let (minutes, hundredthsOfSeconds) = hundredths.quotientAndRemainder(dividingBy: 60 * 100)
let (seconds, milliseconds) = hundredthsOfSeconds.quotientAndRemainder(dividingBy: 100)
minutesLabel.text = String(minutes)
secondsLabel.text = String(format: "%02d", seconds)
milliSecondsLabel.text = String(format: "%02d", milliseconds)
}
func roundButtons() {
resetButton.layer.cornerRadius = resetButton.bounds.height / 2
startButton.layer.cornerRadius = startButton.bounds.height / 2
pauseButton.layer.cornerRadius = pauseButton.bounds.height / 2
}
func setFonts() {
minutesLabel.font = UIFont.monospacedDigitSystemFont(ofSize: minutesLabel.font.pointSize, weight: .regular)
secondsLabel.font = UIFont.monospacedDigitSystemFont(ofSize: secondsLabel.font.pointSize, weight: .regular)
milliSecondsLabel.font = UIFont.monospacedDigitSystemFont(ofSize: milliSecondsLabel.font.pointSize, weight: .regular)
}
}
结果:
完全不相关,但任何依赖于视图大小的东西(例如圆角)确实属于 viewDidLayoutSubviews
,而不属于 viewDidLoad
。
出于某种原因,秒表看起来还不错,但与实际交易相比,它走得太快了。我不明白我应该做什么。是我应该改变的时间间隔吗?我希望它显示毫秒、秒和分钟,但我尝试的一切只会让情况变得更糟。谢谢
import UIKit
class ViewController: UIViewController {
// Outlets
@IBOutlet weak var minutes: UILabel!
@IBOutlet weak var seconds: UILabel!
@IBOutlet weak var milliSecondsLabel: UILabel!
@IBOutlet weak var resetButton: UIButton!
@IBOutlet weak var startButton: UIButton!
@IBOutlet weak var pauseButton: UIButton!
// Variables
var timer = Timer()
var time: Int = 0
var running: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
roundButtons()
}
@IBAction func resetTimer(_ sender: Any) {
timer.invalidate()
time = 0
updateUI()
running = false
}
@IBAction func startTimer(_ sender: Any) {
if running {
return
} else {
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(timerDidEnd), userInfo: nil, repeats: true)
running = true
}
}
@IBAction func pauseTimer(_ sender: Any) {
timer.invalidate()
running = false
}
func roundButtons() {
resetButton.layer.cornerRadius = resetButton.frame.height / 2
startButton.layer.cornerRadius = startButton.frame.height / 2
pauseButton.layer.cornerRadius = pauseButton.frame.height / 2
}
@objc func timerDidEnd() {
time += 1
updateUI()
}
func updateUI() {
var min: Int
var sec: Int
var mil: Int
min = time / (60*60)
sec = (time/60)%60
mil = time & 60
minutes.text = String(min)
seconds.text = String(sec)
milliSecondsLabel.text = String(mil)
}
}
我建议两件事:
不要自己数时间。捕获开始时间并从中计算经过的时间。您可以使用
Date
方法timeIntervalSince
, or, because that's not guaranteed to return monotonically increasing values, useCACurrentMediaTime
,如下所示。与其使用每秒任意更新 100 次的计时器,不如使用
CADisplayLink
,它针对设备屏幕刷新率进行了最佳计时。
例如:
class ViewController: UIViewController {
// Outlets
@IBOutlet weak var minutesLabel: UILabel!
@IBOutlet weak var secondsLabel: UILabel!
@IBOutlet weak var milliSecondsLabel: UILabel!
@IBOutlet weak var resetButton: UIButton!
@IBOutlet weak var startButton: UIButton!
@IBOutlet weak var pauseButton: UIButton!
// Variables
private weak var displayLink: CADisplayLink?
private var startTime: CFTimeInterval?
private var elapsed: CFTimeInterval = 0
private var priorElapsed: CFTimeInterval = 0
override func viewDidLoad() {
super.viewDidLoad()
setFonts()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
roundButtons()
}
@IBAction func resetTimer(_ sender: Any) {
stopDisplayLink()
elapsed = 0
priorElapsed = 0
updateUI()
}
@IBAction func startTimer(_ sender: Any) {
if displayLink == nil {
startDisplayLink()
}
}
@IBAction func pauseTimer(_ sender: Any) {
priorElapsed += elapsed
elapsed = 0
displayLink?.invalidate()
}
}
private extension ViewController {
func startDisplayLink() {
startTime = CACurrentMediaTime()
let displayLink = CADisplayLink(target: self, selector: #selector(handleDisplayLink(_:)))
displayLink.add(to: .main, forMode: .common)
self.displayLink = displayLink
}
func stopDisplayLink() {
displayLink?.invalidate()
}
@objc func handleDisplayLink(_ displayLink: CADisplayLink) {
guard let startTime = startTime else { return }
elapsed = CACurrentMediaTime() - startTime
updateUI()
}
func updateUI() {
let totalElapsed = elapsed + priorElapsed
let hundredths = Int((totalElapsed * 100).rounded())
let (minutes, hundredthsOfSeconds) = hundredths.quotientAndRemainder(dividingBy: 60 * 100)
let (seconds, milliseconds) = hundredthsOfSeconds.quotientAndRemainder(dividingBy: 100)
minutesLabel.text = String(minutes)
secondsLabel.text = String(format: "%02d", seconds)
milliSecondsLabel.text = String(format: "%02d", milliseconds)
}
func roundButtons() {
resetButton.layer.cornerRadius = resetButton.bounds.height / 2
startButton.layer.cornerRadius = startButton.bounds.height / 2
pauseButton.layer.cornerRadius = pauseButton.bounds.height / 2
}
func setFonts() {
minutesLabel.font = UIFont.monospacedDigitSystemFont(ofSize: minutesLabel.font.pointSize, weight: .regular)
secondsLabel.font = UIFont.monospacedDigitSystemFont(ofSize: secondsLabel.font.pointSize, weight: .regular)
milliSecondsLabel.font = UIFont.monospacedDigitSystemFont(ofSize: milliSecondsLabel.font.pointSize, weight: .regular)
}
}
结果:
完全不相关,但任何依赖于视图大小的东西(例如圆角)确实属于 viewDidLayoutSubviews
,而不属于 viewDidLoad
。