如何在后台模式下 运行 NStimer?
how to run NStimer in background Mode?
我正在开发计算员工工作时间的应用程序。
所以我想 运行 一个在后台模式下保持活动状态的计时器,但我没有找到任何直接执行此操作的方法。
我想我可以通过以下策略实现这一目标...
- 在
applicationDidEnterBackground
方法中userDefault
保存当前时间。
- 当应用程序再次激活时,从
userDefault
中获取保存的时间,然后在 applicationWillEnterForeground
中找到与当前时间的差异 method.Then 显示计算的时间。
现在,我想知道这样做是否正确?或者有什么好的方法吗??
谢谢!!
没有理由将当前时间保存在 applicationDidEnterBackground
。
您真正需要的只是用户“开始工作”的时间,或者 nil
如果用户尚未开始工作。每当您需要知道工作时间时,您可以将其计算为当前时间减去 started-work 时间:
let workStartTimeKey = "workStartTime"
class ViewController: UIViewController {
var secondsWorked: CFTimeInterval? {
guard UserDefaults.standard.object(forKey: workStartTimeKey) != nil else { return nil }
let startTime = UserDefaults.standard.double(forKey: workStartTimeKey)
return CFAbsoluteTimeGetCurrent() - startTime
}
(由于iOS SDK的时间单位多以秒为单位,所以存储和计算时最好以秒为单位,而不是小时。如果需要显示或与您的通信时,您可以转换为小时服务器或其他任何东西。)
我假设您的用户界面中有一个按钮供用户在开始工作时点击。点击按钮时,记录当前时间,启动定时器定期更新显示,并立即更新显示:
@IBOutlet var startWorkButton: UIButton!
@IBAction func startWork() {
guard secondsWorked == nil else {
// Already started work.
return
}
UserDefaults.standard.set(CFAbsoluteTimeGetCurrent(), forKey: workStartTimeKey)
startDisplayUpdateTimer()
updateViews()
}
private var displayUpdateTimer: Timer?
private func startDisplayUpdateTimer() {
displayUpdateTimer = Timer(timeInterval: 1, repeats: true, block: { [weak self] _ in
self?.updateViews()
})
RunLoop.main.add(displayUpdateTimer!, forMode: .commonModes)
}
我假设您还有另一个停止工作的按钮。当它被点击时,你抓取 secondsWorked
的当前值,清除存储的 start-work 时间,取消显示更新定时器,并用抓取的 secondsWorked
值做任何你想做的事情。在这里我只打印它:
@IBOutlet var stopWorkButton: UIButton!
@IBAction func stopWork() {
guard let secondsWorked = self.secondsWorked else {
// Didn't start work yet!
return
}
displayUpdateTimer?.invalidate()
displayUpdateTimer = nil
UserDefaults.standard.removeObject(forKey: workStartTimeKey)
updateViews()
print("seconds worked: \(secondsWorked)")
}
要更新显示,您可能希望在标签中显示当前工作时间。您还可以根据用户当前是否在工作来启用或禁用按钮:
@IBOutlet var timeWorkedLabel: UILabel!
private func updateViews() {
if let secondsWorked = secondsWorked {
startWorkButton.isEnabled = false
stopWorkButton.isEnabled = true
timeWorkedLabel.text = timeWorkedFormatter.string(from: secondsWorked)
} else {
startWorkButton.isEnabled = true
stopWorkButton.isEnabled = false
timeWorkedLabel.text = "Not working"
}
}
private let timeWorkedFormatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second]
formatter.allowsFractionalUnits = true
formatter.collapsesLargestUnit = true
formatter.maximumUnitCount = 2
formatter.unitsStyle = .abbreviated
formatter.zeroFormattingBehavior = .dropLeading
return formatter
}()
加载视图时,您应该查看用户是否已经在工作。如果应用程序在她开始工作后被终止并重新启动,就会发生这种情况。如果她已经在工作,请启动显示更新计时器。您还需要更新视图,无论如何:
override func viewDidLoad() {
super.viewDidLoad()
if secondsWorked != nil { startDisplayUpdateTimer() }
updateViews()
}
最后,当视图控制器被销毁时,你应该取消定时器,以防你把它放在允许它被弹出的导航控制器中:
deinit {
displayUpdateTimer?.invalidate()
}
}
我正在开发计算员工工作时间的应用程序。
所以我想 运行 一个在后台模式下保持活动状态的计时器,但我没有找到任何直接执行此操作的方法。
我想我可以通过以下策略实现这一目标...
- 在
applicationDidEnterBackground
方法中userDefault
保存当前时间。 - 当应用程序再次激活时,从
userDefault
中获取保存的时间,然后在applicationWillEnterForeground
中找到与当前时间的差异 method.Then 显示计算的时间。
现在,我想知道这样做是否正确?或者有什么好的方法吗??
谢谢!!
没有理由将当前时间保存在 applicationDidEnterBackground
。
您真正需要的只是用户“开始工作”的时间,或者 nil
如果用户尚未开始工作。每当您需要知道工作时间时,您可以将其计算为当前时间减去 started-work 时间:
let workStartTimeKey = "workStartTime"
class ViewController: UIViewController {
var secondsWorked: CFTimeInterval? {
guard UserDefaults.standard.object(forKey: workStartTimeKey) != nil else { return nil }
let startTime = UserDefaults.standard.double(forKey: workStartTimeKey)
return CFAbsoluteTimeGetCurrent() - startTime
}
(由于iOS SDK的时间单位多以秒为单位,所以存储和计算时最好以秒为单位,而不是小时。如果需要显示或与您的通信时,您可以转换为小时服务器或其他任何东西。)
我假设您的用户界面中有一个按钮供用户在开始工作时点击。点击按钮时,记录当前时间,启动定时器定期更新显示,并立即更新显示:
@IBOutlet var startWorkButton: UIButton!
@IBAction func startWork() {
guard secondsWorked == nil else {
// Already started work.
return
}
UserDefaults.standard.set(CFAbsoluteTimeGetCurrent(), forKey: workStartTimeKey)
startDisplayUpdateTimer()
updateViews()
}
private var displayUpdateTimer: Timer?
private func startDisplayUpdateTimer() {
displayUpdateTimer = Timer(timeInterval: 1, repeats: true, block: { [weak self] _ in
self?.updateViews()
})
RunLoop.main.add(displayUpdateTimer!, forMode: .commonModes)
}
我假设您还有另一个停止工作的按钮。当它被点击时,你抓取 secondsWorked
的当前值,清除存储的 start-work 时间,取消显示更新定时器,并用抓取的 secondsWorked
值做任何你想做的事情。在这里我只打印它:
@IBOutlet var stopWorkButton: UIButton!
@IBAction func stopWork() {
guard let secondsWorked = self.secondsWorked else {
// Didn't start work yet!
return
}
displayUpdateTimer?.invalidate()
displayUpdateTimer = nil
UserDefaults.standard.removeObject(forKey: workStartTimeKey)
updateViews()
print("seconds worked: \(secondsWorked)")
}
要更新显示,您可能希望在标签中显示当前工作时间。您还可以根据用户当前是否在工作来启用或禁用按钮:
@IBOutlet var timeWorkedLabel: UILabel!
private func updateViews() {
if let secondsWorked = secondsWorked {
startWorkButton.isEnabled = false
stopWorkButton.isEnabled = true
timeWorkedLabel.text = timeWorkedFormatter.string(from: secondsWorked)
} else {
startWorkButton.isEnabled = true
stopWorkButton.isEnabled = false
timeWorkedLabel.text = "Not working"
}
}
private let timeWorkedFormatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second]
formatter.allowsFractionalUnits = true
formatter.collapsesLargestUnit = true
formatter.maximumUnitCount = 2
formatter.unitsStyle = .abbreviated
formatter.zeroFormattingBehavior = .dropLeading
return formatter
}()
加载视图时,您应该查看用户是否已经在工作。如果应用程序在她开始工作后被终止并重新启动,就会发生这种情况。如果她已经在工作,请启动显示更新计时器。您还需要更新视图,无论如何:
override func viewDidLoad() {
super.viewDidLoad()
if secondsWorked != nil { startDisplayUpdateTimer() }
updateViews()
}
最后,当视图控制器被销毁时,你应该取消定时器,以防你把它放在允许它被弹出的导航控制器中:
deinit {
displayUpdateTimer?.invalidate()
}
}