我想创建一个计时器,当我切换到另一个ViewController并稍后再返回时,该计时器将继续运行。我的解决方案是这样的:
所以我的问题出在第三步:我想在函数viewDidLoad()
中启动计时器。但是计时器不会在下一个ViewController中启动。
这是我的代码:
var timer = Timer()
var eighthours: Int = 8
var activejob: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
identify_activejob()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(Jobs.jobtime), userInfo: nil, repeats: true)
}
//functions
@objc func jobtime() {
eighthours -= 1
}
答案 0 :(得分:1)
一些想法:
计时器处理程序中的eighthours -= 1
有点问题,因为它假定Timer
会在不中断的情况下以所需的timeInterval
触发。但是您应该在Timer
中适应中断(例如,由于某种原因,UI暂时被阻塞,用户完全离开了应用并返回,等等。)
我们经常从“每次调用计时器处理程序时递减一些计数器”转变为“弄清楚希望计时器到期的时间”。这样可以将“模型”(停止时间)与“视图”(UI更新的频率)分离。这样一来,如果您以后决定以更高的频率(例如,显示毫秒而不是秒,可能使用CADisplayLink
而不是Timer
来更新UI),则不会更改驱动应用的模型。并且使您的应用不受可能影响计时器的短暂中断的侵扰。
如果采用这种模式,则可以绕过这个“停止时间”,模型从视图控制器到视图控制器,每个视图控制器都可以根据该场景所需UX的需要启动和停止自己的计时器。 。
因此,要启动一个将在8秒内停止的计时器:
var stopTime: Date? // set this when you start the timer
func startTimer() {
stopTime = Date().addingTimeInterval(8)
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(jobtime(_:)), userInfo: nil, repeats: true)
}
计时器处理程序可以确定timeIntervalSince
还剩下多少时间来计算两个日期之间的浮点差(以秒为单位)。
@objc func jobtime(_ timer: Timer) {
let now = Date()
guard let stopTime = stopTime, now < stopTime else {
timer.invalidate()
return
}
let timeRemaining = stopTime.timeIntervalSince(now)
...
}
我还使用jobtime
参数更新了timer
,以便您一眼就能看到它是Timer
处理程序。
仅供参考,您的代码为视图控制器引入了强大的参考,这将阻止其被释放。基于选择器的Timer
对其目标保持强烈引用,而运行循环则对Timer
保持引用,因此只有在您invalidate
{{ 1}}。
有两种解决方法:
随着视图的出现和消失,启动和停止计时器:
Timer
请注意,我们在weak var timer: Timer?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(jobtime(_:)), userInfo: nil, repeats: true)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
timer?.invalidate()
}
@objc func jobtime(_ timer: Timer) { ... }
和viewDidAppear
(而不是viewDidDisappear
)中执行此操作,以确保计时器的启动和停止始终保持平衡。
另一种模式是使用基于块的viewDidLoad
,使用Timer
引用来避免让计时器保持对视图控制器的强引用,然后可以[weak self]
使用它在invalidate
方法中:
deinit
最后,如果您想以最大可能的频率(例如显示毫秒)更新UI,则可能会使用weak var timer: Timer?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
self?.jobtime(timer)
}
}
deinit {
timer?.invalidate()
}
这是一个特殊的计时器,它是UI更新的理想时机:
CADisplayLink
但是所有这些方法的共同特征是:(a)我们消除了持久保留的强引用,这些强引用会在适当时干扰视图控制器的释放; (b)我们让每个视图控制器以所需的任意频率更新其UI。
答案 1 :(得分:1)
我可以告诉你一种方法。但是可能会建议,它具有本机设计缺陷,并且使用它取决于准确性。
let timer = Timer.init(timeInterval: 1, repeats: true) { (timer) in
let string = ISO8601DateFormatter().string(from: Date())
print("running" + string)
}
}
class TimerViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
RunLoop.main.add(timer, forMode: RunLoop.Mode.default)
// Do any additional setup after loading the view.
}
}
它将一直运行直到失效。