web-dev-qa-db-ja.com

SwiftUIのWKWebView-ユーザーがWebサイトを操作するときにビューを切り替えるにはどうすればよいですか?

私の状況は次のとおりです。SwiftUIアプリケーションがあり、WebViewを表示したいと考えています。ユーザーがそのWebViewの特定のボタンをタップすると、ユーザーが次の(SwiftUI)ビューにリダイレクトされるようにします。 UIViewRepresentableを使用するのは、これがSwiftUIでWebViewを表示するための現在の方法のように思われるためです。これは、UIKitへのブリッジだからです。問題は、UIViewRepresentableにbodyがないことです。それで、ビューを切り替えるようにどこに指示しますか?通常のSwiftUIビューでは、モデルを更新してから、ボディのモデル変更に反応します。

https://www.google.com がWebViewでレンダリングされる例を設定しました。ユーザーが検索クエリを送信すると、UIViewRepresentableの関数を呼び出すコーディネーターが呼び出されます。

View-これは、別のビューを表示することでモデルの変更に反応するビューです(NavigationLinksで実装)

import SwiftUI

struct WebviewContainer: View {
    @ObservedObject var model: WebviewModel = WebviewModel()
    var body: some View {
        return NavigationView {
            VStack {
                NavigationLink(destination: LoginView(), isActive: $model.loggedOut) {
                    EmptyView()
                }.isDetailLink(false)
                .navigationBarTitle(Text(""))
                .navigationBarHidden(self.model.navbarHidden)

                NavigationLink(destination: CameraView(model: self.model), isActive: $model.shouldRedirectToCameraView) {
                    EmptyView()
                }
                .navigationBarTitle(Text(""))
                .navigationBarHidden(self.model.navbarHidden)
                Webview(model: self.model)
            }
        }
    }
}

IViewControllerRepresentable-これは、SwiftUIコンテキストでWKWebviewを使用するために必要です

import SwiftUI
import WebKit

struct Webview : UIViewControllerRepresentable {
    @ObservedObject var model: WebviewModel

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> EmbeddedWebviewController {
        let webViewController = EmbeddedWebviewController(coordinator: context.coordinator)
        webViewController.loadUrl(URL(string:"https://www.google.com")!)

        return webViewController
    }

    func updateUIViewController(_ uiViewController: EmbeddedWebviewController, context: UIViewControllerRepresentableContext<Webview>) {

    }

    func startCamera() {
        model.startCamera()
    }
}

IViewController-「Google検索」のクリックに反応してコーディネーターを呼び出すWKNavigationDelegate

import UIKit
import WebKit

class EmbeddedWebviewController: UIViewController, WKNavigationDelegate {

    var webview: WKWebView
    var router: WebviewRouter? = nil

    public var delegate: Coordinator? = nil

    init(coordinator: Coordinator) {
        self.delegate = coordinator
        self.webview = WKWebView()
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        self.webview = WKWebView()
        super.init(coder: coder)
    }

    public func loadUrl(_ url: URL) {
        webview.load(URLRequest(url: url))
    }

    override func loadView() {
        self.webview.navigationDelegate = self
        view = webview
    }

    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        guard let url = (navigationResponse.response as! HTTPURLResponse).url else {
            decisionHandler(.cancel)
            return
        }

        if url.absoluteString.starts(with: "https://www.google.com/search?") {
            decisionHandler(.cancel)
            self.delegate?.startCamera(sender: self.webview)
        }
        else {
            decisionHandler(.allow)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

コーディネーター-WKNavigationDelegateUIViewRepresentableの間のブリッジ

import Foundation
import WebKit

class Coordinator: NSObject {
    var webview: Webview

    init(_ webview: Webview) {
        self.webview = webview
    }

    @objc func startCamera(sender: WKWebView) {
        webview.startCamera()
    }
}

[〜#〜]更新[〜#〜]

モデルのあるビュー(@ ObservedObject)ができました。このモデルはIViewControllerRepresentableに与えられます。ユーザーが「Google検索」をクリックすると、IViewControllerRepresentablemodel.startCamera()を正常に呼び出します。ただし、このモデルの変更はWebviewContainerには反映されません。何故ですか? @ ObservedObject sの目的はそれではありませんか?

4
Schnodderbalken

さて、私は非常に激しいデバッグセッションをした後でそれを理解しました:

1.)この投稿で紹介したコードは実際に機能します。 私の周囲のコードのコンテキストでのみ問題がありました

2.)これまでに提供された唯一の回答何も修正しません。それがすでに機能していて、メインスレッドで実行されていないコードとは何の関係もないからです(私は確かに同意しますが、これはUIに影響を与えるアクションに対して実行する必要があることに注意してください)。

3.)私の場合、問題はWebviewContainerにつながるビューにありました。そのビューでは、API呼び出しで値を変更するモデルがありました。成功するとWebviewContainerにリダイレクトすることを決定します。失敗した場合はリダイレクトしません。ここまでは順調ですね。しかし、私はifでモデル変更を行っていたため、-elseでそれを防止する必要がありました。私は他の人を逃していたので、一瞬の間にそれは正しいことをしていて、そして元に戻っていました。私がモデルを見たとき、これはデバッグが困難でした。奇妙なことは、モデルのコンストラクターが2回呼び出されたことだけです。

この場合、私は与えられた答えに報奨金を与えることができないことを残念に思います(あなたは投資された時間の賛成票を獲得します)。

どうもありがとうございました!学習:次に、アプリケーションコードの残りの部分の副作用を減らすために、問題をできるだけ特定するようにしてください。

0
Schnodderbalken