SwiftUI:取消定时器后如何重新启动它?
Posted
技术标签:
【中文标题】SwiftUI:取消定时器后如何重新启动它?【英文标题】:SwiftUI: How do you restart a timer after cancelling it? 【发布时间】:2019-12-09 00:57:42 【问题描述】:我有一个由主 ContentView 和 TimerView 组成的导航视图。 TimerView 有一个计时器,当我调用self.timer.upstream.connect().cancel()
时,它会正确递增并正确停止。
但是,当我返回 ContentView 然后再次导航到 TimerView 时,我希望计时器再次开始计数,但这不会发生。 secondsElapsed
确实重置为 0,但计时器没有运行。
import SwiftUI
struct ContentView: View
var body: some View
NavigationView
NavigationLink(destination: TimerView())
Text("Go to Timer View")
struct TimerView: View
@State var secondsElapsed = 0
var timer = Timer.publish (every: 1, on: .main, in: .common).autoconnect()
var body: some View
VStack
Text("\(self.secondsElapsed) seconds elapsed")
Button("Stop timer",
action:
self.timer.upstream.connect().cancel()
)
.onReceive(timer) _ in
self.secondsElapsed += 1
struct ContentView_Previews: PreviewProvider
static var previews: some View
ContentView()
【问题讨论】:
【参考方案1】:据我所知(并且从历史上看),定时器在取消或失效后无法重新启动。
我所做的只是重新声明计时器。
2021 年 12 月更新:
之前的代码来自 SwiftUI 1,它有一些“奇怪之处”,尤其是在生命周期方面。因此,我将代码更新为在 SwiftUI 3 中的操作方式。
对于此代码,您必须通过import combine
声明Cancellable
类型。
这是一个添加了一些个人偏好的示例:
struct TimerView: View
@State var secondsElapsed = 0
@State var timer: Timer.TimerPublisher = Timer.publish(every: 1, on: .main, in: .common)
@State var connectedTimer: Cancellable? = nil
var body: some View
VStack
Text("\(self.secondsElapsed) seconds elapsed")
Button("Stop Timer", action:
self.cancelTimer()
)
Button("Continue Timer", action:
self.instantiateTimer()
)
Button("Restart Timer", action:
self.restartTimer()
)
.onAppear
self.instantiateTimer()
.onDisappear
self.cancelTimer()
.onReceive(timer) _ in
self.secondsElapsed += 1
func instantiateTimer()
self.timer = Timer.publish(every: 1, on: .main, in: .common)
self.connectedTimer = self.timer.connect()
return
func cancelTimer()
self.connectedTimer?.cancel()
return
func resetCounter()
self.secondsElapsed = 0
return
func restartTimer()
self.secondsElapsed = 0
self.cancelTimer()
self.instantiateTimer()
return
【讨论】:
self.timer.connect()
给出Result of call to 'connect()' is unused
的警告
@Patrick 我回答这个问题已经有一段时间了,但我认为定时器生命周期和实例化Timer
存在一些奇怪之处(例如 .connect() 在 SwiftUI 1 中的行为不同)。尝试更新后的答案并告诉我。【参考方案2】:
在View
中有一个未附加到@State
变量或@ObservedObject
模型的变量似乎没有很好定义的文档 - 但一般规则是没有@987654324 的所有内容@ 注释本质上是一次性的 - 特别是因为 TimerView
是 struct
并且不会在重新渲染时保留。
Timer.TimerPublisher
是一个类 - 所以基本上这可以归结为两个用例
@State
中(注意self
捕获的强保留 - 再次说明onReceive
的文档很薄存储它的连接)
如果它是非瞬态的(即:保留在视图加载/卸载上),那么它需要进入外部模型(或全局var
)并通过init
调用TimerView
引入
你有 @State var secondsElapsed
让我觉得它应该是暂时的
【讨论】:
【参考方案3】:如果你有一个倒数计时器,你可以这样定义它:
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
...并将其用于文本字段,例如:
Text("Punkte: \(Punkte.formatnumber())")
.onReceive(timer) input in
if time < 1 gameOver() else self.time -= TimerToggle ? 1 : 0
所以你只需要在按钮的动作中设置切换:
Button(action: TimerToggle.toggle() , label:
Text("Pause")
)
在 SwiftUI 5.2
中更简单且有效【讨论】:
以上是关于SwiftUI:取消定时器后如何重新启动它?的主要内容,如果未能解决你的问题,请参考以下文章
如何为vertx设置一个持久的计时器,如果服务器重新启动,它不会丢失?