Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/120.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios SwiftUI-可选计时器、重置和重新创建_Ios_Swift_Swiftui_Nstimer_Combine - Fatal编程技术网

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

Ios SwiftUI-可选计时器、重置和重新创建,ios,swift,swiftui,nstimer,combine,Ios,Swift,Swiftui,Nstimer,Combine,通常,我会使用一个可选变量来保存我的计时器引用,因为在重新创建之前能够使无效并将其设置为nil很好 我正在尝试使用SwiftUI,希望确保我的操作正确无误 本人声明: @State var timer:Publishers.Autoconnect<Timer.TimerPublisher>? = nil 要驱动UI文本控件,我使用: .onReceive(timer) { time in print("The time is now \(time)")

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

我正在尝试使用
SwiftUI
,希望确保我的操作正确无误

本人声明:

@State var timer:Publishers.Autoconnect<Timer.TimerPublisher>? = nil
要驱动UI文本控件,我使用:

.onReceive(timer) { time in
    print("The time is now \(time)")
}
使用此
组合
键入的
计时器
进行无效和重新创建的正确方法是什么

我读过一篇文章,应该叫:

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


但是,我是否也需要使无效,或者干脆将
nil
去掉?

没有必要扔掉
TimerPublisher
本身
Timer.publish
创建一个
Timer.TimerPublisher
实例,与所有其他发布者一样,该实例仅在您创建订阅时开始发送值,并且在订阅关闭后立即停止发送

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

因此,在声明时分配
计时器。发布
,但不要
自动连接()。无论何时要启动计时器,请在其上调用
connect
,并将
cancelable
保存在实例属性中。然后,每当您想停止计时器时,在
cancelable
上调用
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
属性并将其显示在视图的
主体中即可。这使您能够将
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
    }
}

非常感谢。快速补充问题,我认为$var(binding)访问只用于@State变量的操作,否则我们只使用var(没有$prefix)?我的代码中没有
$
用法,因为没有传递绑定。从
绑定
的文档中可以看到:“将$prefix应用于属性包装的值将返回其projectedValue,对于状态属性包装,它将返回对该值的绑定。”。因此,是的,当您想要在不同类型之间传递
绑定时,只需要使用
$
前缀。
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
    }
}