在这个问题被标记为this other question的重复之前,我试图了解出版商是如何工作的,因为它的行为方式是我意想不到的。
使用与前面问题的答案相同的例子:
// Let's define the view model with my view...
import Combine
import SwiftUI
class TimerViewModel: ObservableObject {
private let cancellable: AnyCancellable?
let intervalPublisher = Timer.TimerPublisher(
interval: 1.0,
runLoop: .main,
mode: .default)
init() {
self.cancellable = timerPublisher.connect() as? AnyCancellable
}
deinit {
self.cancellable?.cancel()
}
}
struct Clock : View {
@EnvironmentObject var viewModel: TimerViewModel
@State private var currentTime: String = "Initial"
var body: some View {
VStack {
Text(currentTime)
}
.onReceive(timer.intervalPublisher) { newTime in
self.currentTime = String(describing: newTime)
}
}
}
在这个阶段,我想要做的就是我的视图模型直接发布值。我不想宣布观点将接受这些类型的价值观。
理想情况下,我想把我的出版商变成一个真正的出版商。我认为下面的代码会起作用:
// Let's define the view model with my view...
import Combine
import SwiftUI
class TimerViewModel: ObservableObject {
private let cancellable: AnyCancellable?
private let assignCancellable: AnyCancellable?
let intervalPublisher = Timer.TimerPublisher(
interval: 1.0,
runLoop: .main,
mode: .default)
@Published var tick: String = "0:0:0"
init() {
cancellable = intervalPublisher.connect() as? AnyCancellable
assignCancellable = intervalPublisher
.map { new in String(describing: new) }
.assign(to: \TimerViewModel.tick, on: self)
}
deinit {
cancellable?.cancel()
assignCancellable?.cancel()
}
}
struct Clock : View {
@EnvironmentObject var viewModel: TimerViewModel
@State private var currentTime: String = "Initial"
var body: some View {
VStack {
Text(currentTime)
Text(viewModel.tick) // why doesn't this work?
}
.onReceive(timer.intervalPublisher) { newTime in
self.currentTime = String(describing: newTime)
}
}
}
我到底做错了什么?
为什么没有触发?
- 编辑:创建时钟视图后,在
SceneDelegate
上设置环境对象。排除的代码附在下面:*
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
// Create the SwiftUI view that provides the window contents.
let view = Clock().environmentObject(TimerViewModel())
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: view)
self.window = window
window.makeKeyAndVisible()
}
}
3条答案
按热度按时间5f0d552i1#
这和你原来的有点不同,但我希望没有什么重要的变化。
我将viewModel设置为ObservedObject只是为了简化代码。
Timer.Publish方法与AutoConnect一起使计时器更易于使用。我发现,将同一发布服务器与多个订阅服务器一起使用会导致问题,因为第一次取消会杀死发布服务器。
我删除了deinit(),因为取消似乎对订阅者是隐式的。
OnReceive和viewModel的更新之间存在冲突,但将onReceive更改为0.9修复了这一问题。
最后,我发现Combine中的print()方法对于监视管道非常有用。
pexxcrt22#
首先试试
Text("Timer: \(date, style:.timer)")
,它会自动给你一个计数计时器。另外,考虑Timer.publisher
的优点,它是一个可以存储在@State
中的结构,因此您甚至不需要ObservableObject
。将M1N4O1P作为M1N5O1P的优点在于,如果由于某种原因时钟不再显示,则计时器停止并且也被丢弃。
但是,如果您确实决定使用
ObservableObject
,那么您可以简单地执行以下操作:下面是对日期字符串进行更准确更改的另一种方法:
ivqmmu1c3#
“@Environment Object”必须在祖先视图上设置模型对象。
我没看到这个。
所以,我确实重写了你的代码。