如何在SwiftUI中设置addObserver?

62

我该如何在SwiftUI中添加NotificationCenter.default.addObserver

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

#selector的参数指的是未向Objective-C公开的实例方法'VPNDidChangeStatus'

但是当我在函数前添加@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)
}

参观一下这个链接,它可能会有所帮助- https://dev59.com/9VkT5IYBdhLWcg3wKsaq - Abhishek
6个回答

88

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

您可以添加一个发布者并且它仍然可以监听来自应用程序的非 SwiftUI 部分触发的 NSNotification 事件,而无需使用 Combine。

例如,当列表出现并收到来自另一个视图/控制器完成的网络请求或类似内容的通知时,它将更新。

如果出于某种原因需要触发 @objc 函数,则需要使用 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
    }
}

5
这个有效。然后你可以发布这样的通知 -> self.nc.post(name: Notification.Name("RemoteContatcsReceived"), object: nil) 在 xCode 11.5 中,我不需要使用@objc,太好了! - Dave Kozikowski
如果您将其移动到状态对象中,则不会因为预览时状态对象未初始化而损坏预览。 - malhal
1
NotificationCenter.publisher(for:object:)确实使用了Combine。你可以从任何代码中调用Objective C函数。你可能是指定义一个Objective C函数。即使如此,这与UIViewControllerRepresentable或协调器完全无关。你只需要在一个类中定义该函数即可。 - Peter Schorn
结构体 YourSwiftUIView: View {@State var updateYourSwiftUIView: Bool = false...} func loadData() { // 做一些事情updateYourSwiftUIView.toggle() } } - Neph Muw

47

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

有关更多信息,请参阅Apple文档

通知扩展

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"])
        }
    }
}

3
很好的回答!不过有一件事:在Swift中使用通知时,请使用Notification而不是NSNotification。 - Tamás Sengel

16

我使用这个扩展,这样在调用时会更加方便:

/// Extension

extension View {
    func onReceive(
        _ name: Notification.Name,
        center: NotificationCenter = .default,
        object: AnyObject? = nil,
        perform action: @escaping (Notification) -> Void
    ) -> some View {
        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")
}


7

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

苹果文档中可查看更多细节。


6

这对我有用

   let NC = NotificationCenter.default



   self.NC.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil, 
                       using: self.VPNDidChangeStatus)


   func VPNDidChangeStatus(_ notification: Notification) {


    }

1

交换这个

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接