SwiftUI:取消定时器后如何重新启动它?

Jez*_*Jez 12 ios swift swiftui

我有一个由主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()
    }
}
Run Code Online (Sandbox Code Playgroud)

Myk*_*kel 18

据我所知(和历史上),定时器在取消或失效后无法重新启动。

我所做的只是重新声明计时器。

这是一个添加了一些个人偏好的示例:

struct TimerView: View {
    @State var secondsElapsed = 0
    @State var timer: Timer.TimerPublisher = Timer.publish (every: 1, on: .main, in: .common)
    var body: some View {
        VStack {
            Text("\(self.secondsElapsed) seconds elapsed")
            Button("Stop timer",
                   action: {
                    self.cancelTimer() // Just to be a little more DRY/maintenance-friendly, but personal preference.
            })
        }.onAppear(perform: {
            self.instantiateTimer() // You could also consider an optional self.timer variable.
            self.timer.connect() // This allows you to manually connect where you want with greater efficiency, if you don't always want to autostart.
        }).onDisappear(perform: {
            // I don't know your exact flow, but you said you want the timer to restart upon return navigation.  
            // So, I am just assuming you want to stop the timer when you navigate out.  
            // But, you can also easily remove this closure.
            // Also, this may not run as you would intuit.
            self.cancelTimer()
        }).onReceive(timer) { _ in
            self.secondsElapsed += 1
        }
    }

    func instantiateTimer() {
        self.timer = Timer.publish (every: 1, on: .main, in: .common)
        return
    }

    func cancelTimer() {
        self.timer.connect().cancel()
        return
    }
}
Run Code Online (Sandbox Code Playgroud)


And*_*omb 4

在 a 中包含一个View未附加到@State变量或@ObservedObject模型的变量似乎没有很好定义的文档 - 但一般规则是没有注释的所有内容@State/@ObservedObject本质上都是一次性的 - 特别是TimerViewastruct并且不会在重新渲染时保留。

Timer.TimerPublisher是一个类 - 所以本质上这可以归结为两个用例

  • 如果它是瞬态的(即:当视图从屏幕上删除并重新加载时重置),则将计时器放入@State(注意捕获的强保留- 同样,关于如何存储其连接的self文档很少)onReceive
  • 如果它是非瞬态的(即:在视图加载/卸载期间保留),那么它需要进入外部模型(或全局var)并通过init调用引入TimerView

你所拥有的@State var secondsElapsed让我觉得它应该是短暂的