SwiftUI-可选的计时器,重置并重新创建 [英] SwiftUI - Optional Timer, reset and recreate

查看:78
本文介绍了SwiftUI-可选的计时器,重置并重新创建的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通常,我会使用一个可选变量来保存我的 Timer 参考,因为很高兴能够 invalidate 并将其设置为 nil 在重新创建之前.

Normally, I would use an optional variable to hold my Timer reference, as it's nice to be able to invalidate and set it to nil before recreating.

我正在尝试使用 SwiftUI ,并希望确保自己正确使用了...

I'm trying to use SwiftUI and want to make sure I'm correctly doing so...

我声明为:

@State var timer:Publishers.Autoconnect<Timer.TimerPublisher>? = nil

后来我:

self.timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

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

To drive a UI text control I use:

.onReceive(timer) { time in
    print("The time is now \(time)")
}

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

What is the right way with this Combine typed Timer to invalidate and recreate?

我看过应该打给你的电话:

I've read one should call:

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

但是,我是否还需要 invalidate (失效),或者只是 nil ?

However, do I also need to invalidate or simply then nil out?

推荐答案

无需丢弃 TimerPublisher 本身. Timer.publish 创建一个 Timer.TimerPublisher 实例,该实例与所有其他发布者一样,仅在您为其创建订阅时才开始发出值-并且它在订阅已关闭.

There is no need to throw away the TimerPublisher itself. Timer.publish creates a Timer.TimerPublisher instance, which like all other publishers, only starts emitting values when you create a subscription to it - and it stops emitting as soon as the subscription is closed.

因此,您无需重新创建 TimerPublisher ,而只需在需要时重新创建对该订阅的订阅即可.

So instead of recreating the TimerPublisher, you just need to recreate the subscription to it - when the need arises.

因此在声明时分配 Timer.publish ,但不要对其进行 autoconnect().每当您要启动计时器时,请在其上调用 connect 并将 Cancellable 保存在实例属性中.然后,每当您想停止计时器时,都调用 Cancellable 上的 cancel ,并将其设置为 nil .

So assign the Timer.publish on declaration, but don't autoconnect() it. Whenever you want to start the timer, call connect on it and save the Cancellable in an instance property. Then whenever you want to stop the timer, call cancel on the Cancellable and set it to nil.

您可以在下面看到一个具有完全预览功能的视图,该视图可以在5秒后启动计时器,每秒钟更新一次视图,并在30秒后停止流式播放.

You can find below a fully working view with a preview that starts the timer after 5 seconds, updates the view every second and stops streaming after 30 seconds.

可以通过将发布者和订阅存储在视图模型中,然后将其注入视图中来进一步改进.

This can be improved further by storing the publisher and the subscription on a view model and just injecting that into the view.

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
    }
}

使用视图模型,您甚至不需要向视图公开 TimerPublisher (或任何 Publisher ),而只需更新 @已发布属性,并将其显示在视图的 body 中.这使您可以将 timer 声明为 autoconnect ,这意味着您无需手动调用 cancel ,您只需nil 退出订阅引用以停止计时器.

With a view model, you don't even need to expose a TimerPublisher (or any Publisher) to the view, but can simply update an @Published property and display that in the body of your view. This enables you to declare timer as autoconnect, which means you don't manually need to call cancel on it, you can simply nil out the subscription reference to stop the timer.

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
    }
}

这篇关于SwiftUI-可选的计时器,重置并重新创建的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆