在 SwiftUI 中控制嵌套 UIView 的惯用方法是什么

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

我想在 SwiftUI 视图层次结构中显示 WKWebView,并且我想使用 SwiftUI 中实现的按钮来控制 WKWebView。

我可以通过创建一个实现 UIViewRepresentable 并包装 WKWebView 的 SwiftUI WebView class 来做到这一点,就像这样...

struct WebView: UIViewRepresentable {

  func makeUIView(context: UIViewRepresentableContext<WebView>) -> WKWebView {
    return WKWebView(frame: .zero)
  }

  func updateUIView(_ uiView: WKWebView, context: UIViewRepresentableContext<WebView>) {
    // Update WKWebView here if necessary
  }
}

...然后如下使用...


struct BrowserView: View {
  var body: some View {
    VStack {
      WebView()

      // Footer
      HStack {
        Button(action: {
          // When this is called, I want to somehow call .goBack()
          // on the WKWebView.
        }) {
          Image(systemName: "chevron.left")
        }

        ...
      } // HStack
    } // VStack
  }
}

当用户点击按钮时,我想以某种方式在 WKWebView 上调用 goBack。我如何在 SwiftUI 中以惯用的方式执行此操作?

我认为我认为要么脆弱要么不是惯用的 SwiftUI 的方法...

我认为最好的方法是通过在您的父视图上创建一个 PassthroughSubject 来利用 Combine 的强大功能,然后在您的子视图中订阅事件 WebView

首先,在您的 UIViewReprsentable 中创建一个结构,用于捕获您要发送到 WebView:

的可用事件
struct WebView: UIViewRepresentable {

    enum WebEvent {
        case back
        case forward
    }

    let eventPublisher: AnyPublisher<WebEvent, Never>

    init(eventPublisher: AnyPublisher<WebEvent, Never>) {
        self.eventPublisher = eventPublisher

        eventPublisher.sink { event in
            // forward the appropriate event to your underlying WKWebView
        }
    }
}

接下来,在您的父级中,创建 PassthroughSubject 以创建这些事件。

struct ParentView: View {
    private let eventSender = PassthroughSubject<WebView.WebEvent, Never>()

    var body: some View {
        VStack {
            Button("Back") {
                self.eventSender.send(.back)
            }
            Button("Forward") {
                self.eventSender.send(.forward)
            }

            WebView(eventPublisher: eventSender.eraseToAnyPublisher())
        }
    }
}