【问题标题】:SwiftUI: How do you restart a timer after cancelling it?SwiftUI:取消定时器后如何重新启动它?
【发布时间】:2019-12-09 00:57:42
【问题描述】:

我有一个由主 ContentViewTimerView 组成的导航视图。 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()
    }
}

【问题讨论】:

    标签: ios swift swiftui


    【解决方案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 的所有内容@ 注释本质上是一次性的 - 特别是因为 TimerViewstruct 并且不会在重新渲染时保留。

    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

      中更简单且有效

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-06-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-03
        • 2017-08-27
        相关资源
        最近更新 更多