首页 > 解决方案 > SwiftUI 接收自定义事件

问题描述

我有一个ObservableObject使用@Published属性包装器发布一些值的方法。该对象还包含一个计时器。

问题是,如何在执行计时器后立即触发事件并在 SwiftUI 的视图中处理该事件(我更喜欢使用类似的东西onReceive)?已经使用组合框架来发布变化的值,我想正确地实现这个事件触发/处理。但是到目前为止,我所读到的关于 Combine 的所有内容都是关于处理值变化的。但在我的情况下,它只是一个简单的事件(没有任何值)。

我知道我可以简单地使用闭包并在计时器到期时调用它,如果没有更好的类似组合的解决方案,我会这样做。

这是一个非常简单的问题的概念性问题,所以我认为它是自我解释的,而无需我提出代码示例?

标签: swiftswiftuievent-handlingcombine

解决方案


SwiftUI 与 Combine 一起使用的方式是 via .onReceive,它需要一个发布者。一个对象可以公开一个发布者——无论Timer是其他什么——作为一个属性。

通过发出值来组合发布者的工作,如果您只需要发出事件发生的信号,您可以发出()akaVoid值。

顺便说一下,这个对象不必是ObservableObject,但它可以是。

class Foo: ObservableObject {
   let timer = Timer.publish(every: 1, on: .main, in: .default)
                    .autoconnect()
                    .map { _ in } // map to Void
                    .eraseToAnyPublisher() // optional, but a good practice
}

现在,您可以使用.onReceive来订阅timer事件:

struct ContentView: View {
   @StateObject var foo = Foo()
   @State var int: Int = 0
   var body: some View {
       Text("\(int)")
          .onReceive(timer) {
              self.int += 1
          }
   }
}

当然,您不仅限于 TimerPublisher。例如,如果发生了一些随机事件,您可以使用 aPassthroughSubject来发布一个值:

class Foo {
   let eventA: AnyPublisher<Void, Never>
   private let subject = PassthroughSubject<Void, Never>()

   init() {
      
      eventA = subject.eraseToAnyPublisher()
      
      let delay = Double.random(in: 10...100)
      DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
          // something random happened, notify
          self?.subject.send()
      }
   }
}

推荐阅读