SwiftUIでネストされたUIViewを制御する慣用的な方法は何ですか
SwiftUIビュー階層にWKWebViewを表示したいのですが、SwiftUIに実装されているボタンを使用してWKWebViewを制御したいと思います。
これを行うには、 UIViewRepresentable を実装して WKWebView をラップするSwiftUI WebView クラスを作成します。
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ではないと思います…
-
ユーザーがバインディングで「戻る」をクリックした回数を表します。 WebViewにそのバインディングをリッスンさせ、それが増加したら元に戻ります。
-
BrowserView に MutableWebViewAPI のようなオブジェクトを作成させ、構築時に WebView に渡します。 WebView はこのオブジェクトをリッスンするため、 BrowserView が MutableWebViewAPI.goBack() のようなものを呼び出すと、 WebView も同じように動作します。
親ビューに PassthroughSubject を作成し、子 WebView のイベントをサブスクライブすることで、Combineの機能を活用するのが最善の方法だと思います。
まず、 UIViewReprsentable に、 WebView に送信する利用可能なイベントをキャプチャする構造体を作成します。
struct WebView: UIViewReprsentable {
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())
}
}
}
source