SWIFT组合:在可观察对象中使用计时器发布程序

dzhpxtsq  于 2022-10-23  发布在  Swift
关注(0)|答案(3)|浏览(242)

在这个问题被标记为this other question的重复之前,我试图了解出版商是如何工作的,因为它的行为方式是我意想不到的。
使用与前面问题的答案相同的例子:

  1. // Let's define the view model with my view...
  2. import Combine
  3. import SwiftUI
  4. class TimerViewModel: ObservableObject {
  5. private let cancellable: AnyCancellable?
  6. let intervalPublisher = Timer.TimerPublisher(
  7. interval: 1.0,
  8. runLoop: .main,
  9. mode: .default)
  10. init() {
  11. self.cancellable = timerPublisher.connect() as? AnyCancellable
  12. }
  13. deinit {
  14. self.cancellable?.cancel()
  15. }
  16. }
  17. struct Clock : View {
  18. @EnvironmentObject var viewModel: TimerViewModel
  19. @State private var currentTime: String = "Initial"
  20. var body: some View {
  21. VStack {
  22. Text(currentTime)
  23. }
  24. .onReceive(timer.intervalPublisher) { newTime in
  25. self.currentTime = String(describing: newTime)
  26. }
  27. }
  28. }

在这个阶段,我想要做的就是我的视图模型直接发布值。我不想宣布观点将接受这些类型的价值观。
理想情况下,我想把我的出版商变成一个真正的出版商。我认为下面的代码会起作用:

  1. // Let's define the view model with my view...
  2. import Combine
  3. import SwiftUI
  4. class TimerViewModel: ObservableObject {
  5. private let cancellable: AnyCancellable?
  6. private let assignCancellable: AnyCancellable?
  7. let intervalPublisher = Timer.TimerPublisher(
  8. interval: 1.0,
  9. runLoop: .main,
  10. mode: .default)
  11. @Published var tick: String = "0:0:0"
  12. init() {
  13. cancellable = intervalPublisher.connect() as? AnyCancellable
  14. assignCancellable = intervalPublisher
  15. .map { new in String(describing: new) }
  16. .assign(to: \TimerViewModel.tick, on: self)
  17. }
  18. deinit {
  19. cancellable?.cancel()
  20. assignCancellable?.cancel()
  21. }
  22. }
  23. struct Clock : View {
  24. @EnvironmentObject var viewModel: TimerViewModel
  25. @State private var currentTime: String = "Initial"
  26. var body: some View {
  27. VStack {
  28. Text(currentTime)
  29. Text(viewModel.tick) // why doesn't this work?
  30. }
  31. .onReceive(timer.intervalPublisher) { newTime in
  32. self.currentTime = String(describing: newTime)
  33. }
  34. }
  35. }

我到底做错了什么?
为什么没有触发?

  • 编辑:创建时钟视图后,在SceneDelegate上设置环境对象。排除的代码附在下面:*
  1. func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
  2. // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
  3. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
  4. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
  5. // Create the SwiftUI view that provides the window contents.
  6. let view = Clock().environmentObject(TimerViewModel())
  7. // Use a UIHostingController as window root view controller.
  8. if let windowScene = scene as? UIWindowScene {
  9. let window = UIWindow(windowScene: windowScene)
  10. window.rootViewController = UIHostingController(rootView: view)
  11. self.window = window
  12. window.makeKeyAndVisible()
  13. }
  14. }
5f0d552i

5f0d552i1#

这和你原来的有点不同,但我希望没有什么重要的变化。

  1. import Combine
  2. import SwiftUI
  3. class TimerViewModel: ObservableObject {
  4. private var assignCancellable: AnyCancellable? = nil
  5. @Published var tick: String = "0:0:0"
  6. init() {
  7. assignCancellable = Timer.publish(every: 1.0, on: .main, in: .default)
  8. .autoconnect()
  9. .map { String(describing: $0) }
  10. .assign(to: \TimerViewModel.tick, on: self)
  11. }
  12. }
  13. struct ContentView: View {
  14. @State private var currentTime: String = "Initial"
  15. @ObservedObject var viewModel = TimerViewModel()
  16. var body: some View {
  17. VStack {
  18. Text(currentTime)
  19. Text(viewModel.tick) // why doesn't this work?
  20. }
  21. .onReceive(Timer.publish(every: 0.9, on: .main, in: .default).autoconnect(),
  22. perform: {
  23. self.currentTime = String(describing: $0)
  24. }
  25. )
  26. }
  27. }

我将viewModel设置为ObservedObject只是为了简化代码。
Timer.Publish方法与AutoConnect一起使计时器更易于使用。我发现,将同一发布服务器与多个订阅服务器一起使用会导致问题,因为第一次取消会杀死发布服务器。
我删除了deinit(),因为取消似乎对订阅者是隐式的。
OnReceive和viewModel的更新之间存在冲突,但将onReceive更改为0.9修复了这一问题。
最后,我发现Combine中的print()方法对于监视管道非常有用。

展开查看全部
pexxcrt2

pexxcrt22#

首先试试Text("Timer: \(date, style:.timer)"),它会自动给你一个计数计时器。另外,考虑Timer.publisher的优点,它是一个可以存储在@State中的结构,因此您甚至不需要ObservableObject

  1. import Combine
  2. import SwiftUI
  3. struct Clock : View {
  4. @State private var timer = Timer.publish(every: 1, on: .main, in:.common).autoconnect()
  5. @State private var currentTime: String = "Initial"
  6. var body: some View {
  7. VStack {
  8. Text(currentTime)
  9. }
  10. .onReceive(timer) { newTime in
  11. self.currentTime = String(describing: newTime)
  12. }
  13. }
  14. }

将M1N4O1P作为M1N5O1P的优点在于,如果由于某种原因时钟不再显示,则计时器停止并且也被丢弃。
但是,如果您确实决定使用ObservableObject,那么您可以简单地执行以下操作:

  1. class MyTimer : ObservableObject {
  2. var objectWillChange = Timer.publish(every: 1, on: .main, in:.common).autoconnect()
  3. }
  4. struct Clock2: View {
  5. @StateObject var timer = MyTimer() // causes body to run every second
  6. var body: some View {
  7. VStack {
  8. Text("Hello, World! \(Date())")
  9. }
  10. }
  11. }

下面是对日期字符串进行更准确更改的另一种方法:

  1. class MyTimer : ObservableObject {
  2. var timer : Timer? = nil
  3. @Published var timeString = ""
  4. init() {
  5. timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
  6. self.timeString = timer.fireDate.description
  7. }
  8. }
  9. }
  10. struct ContentView: View {
  11. @StateObject var timer = MyTimer()
  12. var body: some View {
  13. VStack {
  14. Text("Hello, World! \(timer.timeString)")
  15. }
  16. }
  17. }
展开查看全部
ivqmmu1c

ivqmmu1c3#

“@Environment Object”必须在祖先视图上设置模型对象。
我没看到这个。
所以,我确实重写了你的代码。

  1. import SwiftUI
  2. import Combine
  3. struct ContentView: View {
  4. let timer = TimerViewModel()
  5. var body: some View {
  6. VStack {
  7. Text("Hello World")
  8. TimerView().environmentObject(timer)
  9. }
  10. }
  11. }
  12. struct TimerView: View {
  13. @EnvironmentObject var timer: TimerViewModel
  14. var body: some View {
  15. Text(timer.time)
  16. }
  17. }
  18. class TimerViewModel: ObservableObject {
  19. @Published var time = "init"
  20. private let innerTimer = Timer.TimerPublisher(interval: 1.0, runLoop: .main, mode: .default)
  21. private let cancellable: Cancellable
  22. private var anyCancellable: AnyCancellable?
  23. init() {
  24. cancellable = innerTimer.connect()
  25. anyCancellable = innerTimer
  26. .map({ $0.description })
  27. .assign(to: \TimerViewModel.time, on: self)
  28. }
  29. deinit {
  30. cancellable.cancel()
  31. anyCancellable?.cancel()
  32. }
  33. }
展开查看全部

相关问题