我正在开发能够计算员工工作时间的应用程序 所以我想运行一个在backGround模式下保持活着的计时器,但我没有找到任何方法如何直接这样做。
我想我可以通过遵循策略实现这一目标......
userDefault
方法{/ 1}}保存当前时间。applicationDidEnterBackground
获取保存时间,然后在userDefault
方法中找到与当前时间的差异。然后显示计算出的时间。现在,我想知道,这是正确的方法吗?还是有什么最好的方法? 谢谢!
答案 0 :(得分:2)
没有理由将当前时间保存在applicationDidEnterBackground
。
您在任何时候真正需要的是用户“开始工作”的时间,或者nil
如果用户尚未开始工作。每当您需要知道工作时间时,您可以将其计算为当前时间减去开始工作时间:
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主要使用第二个作为时间单位,因此最好使用秒而不是几小时来存储和计算。如果需要显示或与服务器或其他任何内容通信时,可以转换为小时数。)< / p>
我假设您的用户界面中有一个按钮供用户在开始工作时点按。点击按钮时,您可以记录当前时间,启动计时器以定期更新显示,并立即更新显示:
@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
,清除存储的开始工作时间,取消显示更新计时器,并使用抓取的值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()
}
}