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

标签 ios swift swiftui nstimer combine

通常,我会使用一个可选变量来保存我的 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)")
}
什么是正确的方法 Combine输入 Timer无效并重新创建?
我读过一个应该调用:self.timer.upstream.connect().cancel()但是,我是否还需要无效 或者干脆然后 nil出去?

最佳答案

没有必要扔掉TimerPublisher本身。 Timer.publish创建一个 Timer.TimerPublisher实例,与所有其他发布者一样,仅在您创建订阅时才开始发出值 - 一旦订阅关闭,它就会停止发出。
因此,与其重新创建 TimerPublisher ,您只需要重新创建对它的订阅 - 当需要时。
所以分配 Timer.publish在声明时,但不要 autoconnect()它。每当您想启动计时器时,请调用 connect并保存 Cancellable在实例属性中。然后,每当您想停止计时器时,请调用 cancelCancellable并将其设置为 nil .
您可以在下面找到一个完整的工作 View ,其中带有预览,该 View 在 5 秒后启动计时器,每秒更新 View 并在 30 秒后停止流式传输。
这可以通过将发布者和订阅存储在 View 模型上并将其注入(inject) 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
    }
}
使用 View 模型,您甚至不需要公开 TimerPublisher (或任何 Publisher )到 View ,但可以简单地更新 @Published属性并显示在 body 中你的看法。这使您可以声明 timerautoconnect ,这意味着您无需手动调用 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
    }
}

关于ios - SwiftUI - 可选计时器,重置和重新创建,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62678977/

相关文章:

ios - 获取错误JSON.stringify()ing参数:TypeError:JSON.stringify无法在IOS模拟器中序列化循环结构

ios - cell.imageView 返回 nil

swift - 保存 Bool/tableView 复选标记 - 第三次幸运

ios - DropboxSDK : unable to link

ios - Parse & Swift- 随机化图像

swift - ReactiveSwift : how to rebind (remove old bindings, 添加新的)?

ios - 想要将 uitableview 打印为 pdf

swift - 如何在 SwiftUI 中从图库中选择图像

ios - 如何在没有 NavigationLink 的情况下使用 SwiftUI 推送新的 Root View ?

ios - 当用户点击 SwiftUI 中的列表行时如何显示警报