【问题标题】:How to set addObserver in SwiftUI?如何在 SwiftUI 中设置 addObserver?
【发布时间】:2020-03-08 03:00:42
【问题描述】:

如何在 SwiftUI 中添加 NotificationCenter.default.addObserve

当我尝试添加观察者时,出现以下错误

“#selector”的参数指的是实例方法“VPNDidChangeStatus” 没有暴露给 Objective-C

但是当我在 func 前面添加 @objc 时,我得到以下错误

@objc 只能与类成员、@objc 协议和 类的具体扩展

这是我的代码

let NC = NotificationCenter.default

var body: some View {
     VStack() {

     }.onAppear {

           self.NC.addObserver(self, selector: #selector(self.VPNDidChangeStatus),
                              name: .NEVPNStatusDidChange, object: nil)

     }
} 

@objc func VPNDidChangeStatus(_ notification: Notification) {
    //    print("VPNDidChangeStatus", VPNManager.shared.status)
}

【问题讨论】:

标签: swift swiftui nsnotificationcenter


【解决方案1】:

交换这个

self.NC.addObserver(self, selector: #selector(self.VPNDidChangeStatus),
                          name: .NEVPNStatusDidChange, object: nil) 

self.NC.addObserver(self, selector: #selector(VPNDidChangeStatus(_:)),
                          name: .NEVPNStatusDidChange, object: nil)

【讨论】:

    【解决方案2】:

    这不是 SwiftUI 原生的方法,它是声明式和反应式的。相反,您应该使用 Combine 中的 NSNotificationCenter.publisher(for:object:)。

    Apple Documentation查看更多详情

    【讨论】:

      【解决方案3】:

      这对我有用

         let NC = NotificationCenter.default
      
      
      
         self.NC.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil, 
                             using: self.VPNDidChangeStatus)
      
      
         func VPNDidChangeStatus(_ notification: Notification) {
      
      
          }
      

      【讨论】:

        【解决方案4】:

        接受的答案可能有效,但实际上并不是您应该这样做。在 SwiftUI 中,您不需要以这种方式添加观察者。

        您添加一个发布者,它仍然可以侦听从应用程序的非 SwiftUI 部分触发的 NSNotification 事件,而无需合并。

        这里作为一个例子,列表将在它出现时更新,当它收到通知时,来自另一个视图/控制器上的已完成网络请求或类似的东西等。

        如果您出于某种原因需要触发@objc func,则需要使用UIViewControllerRepresentable 创建一个Coordinator

        struct YourSwiftUIView: View {
        
            let pub = NotificationCenter.default
                    .publisher(for: NSNotification.Name("YourNameHere"))
        
        
            var body: some View {
                List() {
                    ForEach(userData.viewModels) { viewModel in
                        SomeRow(viewModel: viewModel)
                    }
                }
                .onAppear(perform: loadData)
                .onReceive(pub) { (output) in
                    self.loadData()
                }
            }
        
            func loadData() {
                // do stuff
            }
        }
        

        【讨论】:

        • 这行得通。然后,您可以像这样发布通知 -> self.nc.post(name: Notification.Name("RemoteContatcsReceived"), object: nil) 使用 xCode 11.5 我不必使用 @objc 耶!
        • 如果你将它移动到一个状态对象中,那么它不会搞砸你的预览,因为状态对象在预览时没有初始化。
        • NotificationCenter.publisher(for:object:) 确实使用Combine。您可以从任何您想要的代码中调用一个Objective C 函数。大概您指的是定义一个目标C函数。即使这与UIViewControllerRepresentable 或协调员完全无关。你只需要在一个类中定义函数。
        • struct YourSwiftUIView: View { @State updateYourSwiftUIView: Bool = false ... } func loadData() { // 做些事情 updateYourSwiftUIView.toggle() } }
        【解决方案5】:

        我有一种在SwiftUI 中使用NotificationCenter 的方法。

        欲了解更多信息Apple Documentation

        通知扩展n

        extension NSNotification {
            static let ImageClick = Notification.Name.init("ImageClick")
        }
        

        内容视图

        struct ContentView: View {
            var body: some View {
                VStack {
                    DetailView()
                }
                .onReceive(NotificationCenter.default.publisher(for: NSNotification.ImageClick))
                { obj in
                   // Change key as per your "userInfo"
                    if let userInfo = obj.userInfo, let info = userInfo["info"] {
                      print(info)
                   }
                }
            }
        }
        

        详细视图

        struct DetailView: View {
            var body: some View {
                Image(systemName: "wifi")
                    .frame(width: 30,height: 30, alignment: .center)
                    .foregroundColor(.black)
                    .onTapGesture {
                        NotificationCenter.default.post(name: NSNotification.ImageClick, 
                                                        object: nil, userInfo: ["info": "Test"])
                }
            }
        }
        

        【讨论】:

        • 很好的答案!一件事:在 Swift 中使用 Notification 而不是 NSNotification。
        【解决方案6】:

        我使用这个扩展,所以它在呼叫站点上更好一点:

        /// Extension
        
        extension View {
            func onReceive(_ name: Notification.Name,
                           center: NotificationCenter = .default,
                           object: AnyObject? = nil,
                           perform action: @escaping (Notification) -> Void) -> some View {
                self.onReceive(
                    center.publisher(for: name, object: object), perform: action
                )
            }
        }
        
        /// Usage
        
        struct MyView: View {
            var body: some View {
                Color.orange
                    .onReceive(.myNotification) { _ in
                        print(#function)
                    }
            }
        }
        
        extension Notification.Name {
            static let myNotification = Notification.Name("myNotification")
        }
        
        

        【讨论】:

          猜你喜欢
          • 2023-04-05
          • 2021-12-23
          • 1970-01-01
          • 2020-07-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-12-21
          • 2019-12-12
          相关资源
          最近更新 更多