【问题标题】:Publish a message from child to parent View将消息从子视图发布到父视图
【发布时间】:2019-12-20 06:09:49
【问题描述】:

我正试图将这个问题的答案颠倒过来:

What's the idiomatic way to control a nested UIView in SwiftUI

并从子 (WebView) 发布事件并在 ParentView 中订阅。

我可以在WebView的构造函数中传递eventSender,比如:

WebView(eventSender: eventSender)

那么WebView 应该可以发送事件,但是如何订阅ParentView 中发布的事件呢?

有没有人有一个优雅的解决方案?

通常我会使用@EnvironmentObject,但这些似乎不适用于UIViewRepresentable

更新 - 我在这里创建了问题的要点:https://gist.github.com/mattlaver/45519a58de65a304b10acaee663b06a2

请注意,我的 childView 是一个类,而不是一个结构,因为它需要从 NSObject 继承才能使用 navigationDelegate。

我想关闭这个视图(导航回到它在 navigationDelegate 启动时显示之前)。

【问题讨论】:

    标签: swiftui combine


    【解决方案1】:

    如果您希望将状态从 UiViewRepresentable 传递出去,绑定是可行的方法,但您需要从协调器中设置它。例如:

    struct WebView: UIViewRepresentable {
    
      @Binding var state: Int // this can be whatever type
    
      func makeUIView(context: UIViewRepresentableContext<WebView>) -> WKWebView {
        let uiView = WKWebView()
        uiView.navigationDelegate = context.coordinator
        self.state = 0
        return uiView
      }
    
      func updateUIView(_ uiView: WKWebView, context: UIViewRepresentableContext<WebView>) {
      }
    
      func makeCoordinator() -> WebView.Coordinator {
        Coordinator(view: self)
      }
    
      class Coordinator: NSObject, WKNavigationDelegate {
    
        let view: WebView
    
        init(view: WebView) {
          self.view = view
        }
    
        func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
          self.view.state = 1 // the binding will update the parent
          decisionHandler(.allow)
        }
    
      }
    
    }
    

    【讨论】:

      【解决方案2】:

      您可以构建自己的 Binding 并传递您需要的任何对象,@EnvironmentObject@ObservedObject

      看起来@FetchRequestUIViewRepresentable 中运行良好(如果您使用的是CoreData

      struct ContentView: View {
          @EnvironmentObject var store: Store<AppState, AppAction> 
      
          var body: some View {
              WebViewRepresentable(state: Binding<AppState>(get: { self.store.state }, set { _ in}))
          }
      }
      
      struct WebViewRepresentable: UIViewRepresentable {
            @Binding var state: AppState
            [...]
      }
      

      来源:https://gist.github.com/svanimpe/152e6539cd371a9ae0cfee42b374d7c4

      【讨论】:

      • 我尝试过使用 EnvironmentObject 和 ObservedObject 并且它们与我的视图一起工作得很好,但是使用 UIViewRepresentable 它们似乎总是为零,所以在这种情况下我无法使用它们。这就是导致我结合作为一种可能的解决方案来通知父母孩子 WKWebView 已完成的原因。
      【解决方案3】:

      我在这里回答了这个问题:How to access content view's elements later in SwiftUI?

      (我也是您引用的帖子中接受的另一个答案)

      您基本上想要设置一个传入的处理程序,这是 SwiftUI 经常使用的一种方法(请参阅TextField 各种事件的传入处理程序选项。)

      struct WebViewRepresentable: UIViewRepresentable {
        let onEvent: (AppState) -> Void
      
        // not sure what events you're talking about, probably `WKWebView` specific so tailor this to those needs
        fun eventReceived(event: AppState) {
          self.onEvent(event)
        }
      

      然后在你的父视图中:

      var body: some View {
        WebViewRepresentable { event in
          // handle your event
        }
      }
      

      【讨论】:

      • 我似乎无法将您的答案应用于我的代码。 child 是一个类,因为它需要从 NSObject 继承才能从 navigationDelegate 捕获导航。我需要关闭在 navigationDelegate 中捕获的事件的子视图。我在上面添加了一个要点。
      • 它是class 还是struct 并不重要(尽管您似乎将UIViewRepresentableCoordinator 与自身混为一谈。您可能想要制作@987654329 @ 一个结构并让你的Coordiantor 成为NSObject 子类。这就是它存在的原因;) ) 关键是,你将一个闭包从ParentView 传递给ChildView,一个闭包ChildView 调用时它接收某些事件。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-06
      相关资源
      最近更新 更多