SwiftUI - 可选计时器,重置和重新创建

Woo*_*ock 2 nstimer ios swift swiftui combine

通常,我会使用一个可选变量来保存我的Timer引用,因为能够在重新创建之前使其无效并将其设置为很好nil

我正在尝试使用SwiftUI并希望确保我这样做是正确的......

我声明为:

@State var timer:Publishers.Autoconnect<Timer.TimerPublisher>? = nil
Run Code Online (Sandbox Code Playgroud)

后来我:

self.timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
Run Code Online (Sandbox Code Playgroud)

要驱动 UI 文本控件,我使用:

.onReceive(timer) { time in
    print("The time is now \(time)")
}
Run Code Online (Sandbox Code Playgroud)

使用此Combine类型Timer无效并重新创建的正确方法是什么?

我读过一个应该调用:

self.timer.upstream.connect().cancel()

但是,我是否还需要无效或简单地nil退出?

Dáv*_*tor 5

没有必要扔掉TimerPublisher它本身。Timer.publish创建一个Timer.TimerPublisher实例,与所有其他发布者一样,仅在您创建订阅时才开始发出值 - 并且一旦订阅关闭,它就会停止发出。

因此TimerPublisher,无需重新创建 ,您只需要重新创建对它的订阅 - 当需要时。

所以分配Timer.publishon 声明,但不要这样做autoconnect()。每当您想启动计时器时,请调用connect它并将其保存Cancellable在实例属性中。然后,只要你想停止计时,通话cancelCancellable并将其设置为nil

您可以在下面找到一个完整的工作视图,其中带有预览,该视图在 5 秒后启动计时器,每秒更新视图并在 30 秒后停止流式传输。

这可以通过将发布者和订阅存储在视图模型上并将其注入视图来进一步改进。

struct TimerView: View {
    @State private var text: String = "Not started"

    private var timerSubscription: Cancellable?

    private let timer = Timer.publish(every: 1, on: .main, in: .common)

    var body: some View {
        Text(text)
            .onReceive(timer) {
                self.text = "The time is now \($0)"
            }
    }

    mutating func startTimer() {
        timerSubscription = timer.connect()
    }

    mutating func stopTimer() {
        timerSubscription?.cancel()
        timerSubscription = nil
    }
}

struct TimerView_Previews: PreviewProvider {
    static var previews: some View {
        var timerView = TimerView()
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            timerView.startTimer()
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 30) {
            timerView.stopTimer()
        }
        return timerView
    }
}
Run Code Online (Sandbox Code Playgroud)

使用视图模型,您甚至不需要向视图公开 a TimerPublisher(或任何Publisher),而只需更新一个@Published属性并将其显示在body您的视图中。这使您能够声明timeras autoconnect,这意味着您无需手动调用cancel它,您只需nil退出订阅引用即可停止计时器。

class TimerViewModel: ObservableObject {
    private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

    private var timerSubscription: Cancellable?

    @Published var time: Date = Date()

    func startTimer() {
        timerSubscription = timer.assign(to: \.time, on: self)
    }

    func stopTimer() {
        timerSubscription = nil
    }
}

struct TimerView: View {
    @ObservedObject var viewModel: TimerViewModel

    var body: some View {
        Text(viewModel.time.description)
    }
}

struct TimerView_Previews: PreviewProvider {
    static var previews: some View {
        let viewModel = TimerViewModel()
        let timerView = TimerView(viewModel: viewModel)
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            viewModel.startTimer()
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 30) {
            viewModel.stopTimer()
        }
        return timerView
    }
}
Run Code Online (Sandbox Code Playgroud)