ios - 使用 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)
}
解决方案
我在您的代码中看到了一些问题,这些问题阻止了它按照您想要的方式工作。
首先,如果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
因为我相信它会在不引入另一个变量的情况下实现相同的效果。
唯一可能剩下的就是关于您正在显示的标签的文本。我不确定您实际上想在此处显示什么。如果您可以在评论中添加它,我可以建议一种方法。
推荐阅读
- mysql - MySQL子查询作为别名 - 未知列错误
- c# - AutoMapper 不映射嵌套的复杂类型
- ffmpeg - 使用 ffmpeg 将 DTS 转换为 AAC 将语音音频向右移动
- path - 路径问题 - POSIX HFS 无法获取文件夹
- java - Firebase 数据库分页滚动动作
- bash - Bash - 如果主域在文件 A(或管道)中,则从文件 B 中删除域/子域
- android - 将可见性设置为 GONE 不会隐藏 imageView
- c# - glBufferData 不断失败并显示 GL_INVALID_ENUM 即使它不应该
- mongodb - 在 MongoDB 中插入大数据
- c# - 如何在 C# 中获取此子字符串结果