我已经构建了一个基本的计时器应用程序,我正在尝试弄清楚如何在后台运行计时器。我已经尝试过签名和功能的后台模式,但似乎不适合我。
我目前正在开发 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()
}
}
})
}
}
Run Code Online (Sandbox Code Playgroud)
如果你们中的任何人对更好地管理计时器有任何建议,请告诉我。谢谢。
Rob*_*Rob 15
当用户离开应用程序时,它会被暂停。当用户离开应用程序时,通常不会\xe2\x80\x99 保持计时器继续运行。我们不想耗尽用户\xe2\x80\x99的电池来更新在用户返回应用程序之前\xe2\x80\x99真正相关的计时器。
\n这显然意味着您不想使用 \xe2\x80\x9ccounter\xe2\x80\x9d 模式。相反,捕获Date启动计时器的时间,并保存它,以防用户离开应用程序:
func saveStartTime() {\n if let startTime = startTime {\n UserDefaults.standard.set(startTime, forKey: "startTime")\n } else {\n UserDefaults.standard.removeObject(forKey: "startTime")\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n并且,当应用程序启动时,检索保存的startTime:
func fetchStartTime() -> Date? {\n UserDefaults.standard.object(forKey: "startTime") as? Date\n}\nRun Code Online (Sandbox Code Playgroud)\n你的计时器现在不应该使用计数器,而是计算开始时间和现在之间经过的时间:
\nlet now = Date()\nlet elapsed = now.timeIntervalSince(startTime)\n\nguard elapsed < 15 else {\n self.stop()\n return\n}\n\nself.message = String(format: "%0.1f", elapsed)\nRun Code Online (Sandbox Code Playgroud)\n就我个人而言,我会从以下内容中抽象出这个计时器和持久性内容View:
class Stopwatch: ObservableObject {\n /// String to show in UI\n @Published private(set) var message = "Not running"\n\n /// Is the timer running?\n @Published private(set) var isRunning = false\n\n /// Time that we're counting from\n private var startTime: Date? { didSet { saveStartTime() } }\n\n /// The timer\n private var timer: AnyCancellable?\n\n init() {\n startTime = fetchStartTime()\n\n if startTime != nil {\n start()\n }\n }\n}\n\n// MARK: - Public Interface\n\nextension Stopwatch {\n func start() {\n timer?.cancel() // cancel timer if any\n\n if startTime == nil {\n startTime = Date()\n }\n\n message = ""\n\n timer = Timer\n .publish(every: 0.1, on: .main, in: .common)\n .autoconnect()\n .sink { [weak self] _ in\n guard\n let self = self,\n let startTime = self.startTime\n else { return }\n\n let now = Date()\n let elapsed = now.timeIntervalSince(startTime)\n\n guard elapsed < 60 else {\n self.stop()\n return\n }\n\n self.message = String(format: "%0.1f", elapsed)\n }\n\n isRunning = true\n }\n\n func stop() {\n timer?.cancel()\n timer = nil\n startTime = nil\n isRunning = false\n message = "Not running"\n }\n}\n\n// MARK: - Private implementation\n\nprivate extension Stopwatch {\n func saveStartTime() {\n if let startTime = startTime {\n UserDefaults.standard.set(startTime, forKey: "startTime")\n } else {\n UserDefaults.standard.removeObject(forKey: "startTime")\n }\n }\n\n func fetchStartTime() -> Date? {\n UserDefaults.standard.object(forKey: "startTime") as? Date\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n然后视图就可以使用这个Stopwatch:
struct ContentView: View {\n @ObservedObject var stopwatch = Stopwatch()\n\n var body: some View {\n VStack {\n Text(stopwatch.message)\n Button(stopwatch.isRunning ? "Stop" : "Start") {\n if stopwatch.isRunning {\n stopwatch.stop()\n } else {\n stopwatch.start()\n }\n }\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\nFWIW,UserDefaults可能不是存储这个的正确位置startTime。我可能会使用 plist 或 CoreData 或其他什么。但我想让上面的内容尽可能简单,以说明持久化的想法,startTime以便当应用程序再次启动时,您可以使其看起来像计时器在后台运行,即使它是 \xe2\x80\ x99t。