我已经建立了一个基本的计时器应用程序,并且试图弄清楚如何在后台运行计时器。
我已经从签名和功能中尝试了背景模式,但似乎不适合我。
我目前正在研究Xcode 12 beta 6。
代码
struct ContentView: View {
@State var start = false
@State var count = 0
var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
ZStack {
// App content.
}
.onReceive(timer, perform: { _ in
if start {
if count < 15 {
count += 1
} else {
start.toggle()
}
}
})
}
}
如果您有任何建议以更好的方式管理计时器,请告诉我。谢谢。 最佳答案
当用户离开该应用程序时,该应用程序将被暂停。通常,当用户离开应用程序时,计时器不会持续计时。在用户返回应用之前,我们不想浪费用户的电池来更新与计时器无关的计时器。
显然,这意味着您不想使用“计数器”模式。而是在启动计时器时捕获Date
,并保存它,以防用户离开应用程序:
func saveStartTime() {
if let startTime = startTime {
UserDefaults.standard.set(startTime, forKey: "startTime")
} else {
UserDefaults.standard.removeObject(forKey: "startTime")
}
}
并且,当应用启动时,检索保存的startTime
:func fetchStartTime() -> Date? {
UserDefaults.standard.object(forKey: "startTime") as? Date
}
而且您的计时器现在不应该使用计数器,而应该计算从开始时间到现在的经过时间:let now = Date()
let elapsed = now.timeIntervalSince(startTime)
guard elapsed < 15 else {
self.stop()
return
}
self.message = String(format: "%0.1f", elapsed)
就个人而言,我将从
View
中抽象出此计时器和持久性内容:class Stopwatch: ObservableObject {
/// String to show in UI
@Published private(set) var message = "Not running"
/// Is the timer running?
@Published private(set) var isRunning = false
/// Time that we're counting from
private var startTime: Date? { didSet { saveStartTime() } }
/// The timer
private var timer: AnyCancellable?
init() {
startTime = fetchStartTime()
if startTime != nil {
start()
}
}
}
// MARK: - Public Interface
extension Stopwatch {
func start() {
timer?.cancel() // cancel timer if any
if startTime == nil {
startTime = Date()
}
message = ""
timer = Timer
.publish(every: 0.1, on: .main, in: .common)
.autoconnect()
.sink { [weak self] _ in
guard
let self = self,
let startTime = self.startTime
else { return }
let now = Date()
let elapsed = now.timeIntervalSince(startTime)
guard elapsed < 60 else {
self.stop()
return
}
self.message = String(format: "%0.1f", elapsed)
}
isRunning = true
}
func stop() {
timer?.cancel()
timer = nil
startTime = nil
isRunning = false
message = "Not running"
}
}
// MARK: - Private implementation
private extension Stopwatch {
func saveStartTime() {
if let startTime = startTime {
UserDefaults.standard.set(startTime, forKey: "startTime")
} else {
UserDefaults.standard.removeObject(forKey: "startTime")
}
}
func fetchStartTime() -> Date? {
UserDefaults.standard.object(forKey: "startTime") as? Date
}
}
然后,视图可以只使用以下Stopwatch
:struct ContentView: View {
@ObservedObject var stopwatch = Stopwatch()
var body: some View {
VStack {
Text(stopwatch.message)
Button(stopwatch.isRunning ? "Stop" : "Start") {
if stopwatch.isRunning {
stopwatch.stop()
} else {
stopwatch.start()
}
}
}
}
}
FWIW,
UserDefaults
可能不是存储此startTime
的正确位置。我可能会使用plist或CoreData或其他任何东西。但是我想让上面的内容尽可能简单,以说明持久startTime
的想法,这样,当应用再次启动时,您可以使它看起来像是计时器在后台运行,即使不是。关于ios - SwiftUI:如何在后台运行计时器,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/63765532/