问题描述
这里我有一个简单的 SwiftUI 项目,它有两个计时器发布器,它们分别设置为每 1 秒和 2 秒触发一次.
Here I have a simple SwiftUI project which has two timer publishers which are set to fire every 1 second and 2 seconds respectively.
预期行为是第一个标签每秒更新一次,第二个标签每 2 秒更新一次.然而,实际发生的只是第一个标签每秒更新一次,而第二个标签无限期地保持为 0.
The expected behavior is that the first label would update every second and the second label would update every 2 seconds. However what actually happens is only the first label updates every second and the second label remains at 0 indefinitely.
我知道使用 Timer.scheduledTimer(withTimeInterval:)
可以通过简单地创建这些计时器实例的新变量来创建多个计时器,但它似乎与这些发布者的工作方式不同.
I know it's possible to make multiple timers using Timer.scheduledTimer(withTimeInterval:)
by simply making new variables of those timer instances but it doesn't seem to work the same way with these publishers.
如何让两个定时器都工作?
How can I make both timers work?
import SwiftUI
struct ContentView: View {
@State private var counter1 = 0
@State private var counter2 = 0
let timer1 = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
let timer2 = Timer.publish(every: 2, on: .main, in: .common).autoconnect()
var body: some View {
VStack(spacing: 30) {
Text(String(counter1))
.frame(width: 50, height: 20)
Text(String(counter2))
.frame(width: 50, height: 20)
}
.padding()
.onReceive(timer1) {_ in
counter1 += 1
}
.onReceive(timer2) {_ in
counter2 += 2
}
}
}
推荐答案
在 SwiftUI 中,您的视图是一个临时结构.即系统一直在重新制作视图并丢弃旧的视图.因此,视图不应持有对计时器的引用(您将一直创建新计时器并丢弃旧计时器).创建一个 viewModel 并将值存储在那里.通常,不要在视图中存储不在属性包装器中或通过初始化程序传入的内容.
In SwiftUI your view is a transitory struct. Ie the system remakes the views all the time and throws away the old ones. The View, therefore should not be holding the references to the timers (you will be making new timers and dropping old ones all the time). Make a viewModel and store the values there. In general don't store things on the view that are not in a property wrapper or passed in through the initializer.
final class ViewModel: ObservableObject {
@Published private(set) var first = 0
@Published private(set) var second = 0
private var subscriptions: Set<AnyCancellable> = []
func start() {
Timer.publish(every: 1, on: .main, in: .common)
.autoconnect()
.scan(0) { accumulated, _ in accumulated + 1 }
.assign(to: \.first, on: self)
.store(in: &subscriptions)
Timer.publish(every: 2, on: .main, in: .common)
.autoconnect()
.scan(0) { accumulated, _ in accumulated + 1 }
.assign(to: \.second, on: self)
.store(in: &subscriptions)
}
func stop() {
subscriptions.removeAll()
}
}
struct ContentView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
VStack {
Text(String(describing: viewModel.first))
Text(String(describing: viewModel.second))
}
.onAppear { viewModel.start() }
.onDisappear { viewModel.stop() }
}
}
这篇关于如何设置多个组合计时器发布者?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!