提问者:小点点

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


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

我正在尝试使用Swiftui,并希望确保操作正确。。。

我宣布:

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

后来我:

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

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

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

使用此组合键入的计时器使其无效和重新创建的正确方法是什么?

我读过一个应该叫:

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

但是,我是否也需要使nilout无效?


共1个答案

匿名用户

没有必要丢弃TimerPublisher本身。 Timer.Publish创建一个Timer.TimerPublisher实例,该实例与所有其他发布服务器一样,仅在您创建对它的订阅时才开始发出值,并且一旦订阅关闭,它就会停止发出值。

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

因此,在声明上分配timer.publish,但不要autoconnect()它。 无论何时要启动计时器,请调用计时器上的connect并将cancellable保存在实例属性中。 然后无论何时要停止计时器,调用cancellable上的cancel并将其设置为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
    }
}

使用视图模型,您甚至不需要向视图公开TimerPublisher(或任何Publisher),而只需更新@published属性并将其显示在视图的body中。 这使您可以将timer声明为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
    }
}