簡體   English   中英

SwiftUI 中的 WKWebView - 用戶與網站交互時如何切換視圖?

[英]WKWebView in SwiftUI - How do I switch the view when user interacts with the website?

我的情況如下:我有一個 SwiftUI 應用程序並想顯示一個 WebView。 當用戶點擊 WebView 中的某個按鈕時,我希望將用戶重定向到下一個(SwiftUI)視圖。 I use a UIViewRepresentable as this seems to be the current way to go for showing a WebView in SwiftUI because it's the bridge to UIKit. 問題是: UIViewRepresentable 沒有body 那么我在哪里告訴視圖切換呢? 在通常的 SwiftUI 視圖中,我會有一個 model ,我會對其進行更新,然后對身體中的 model 變化做出反應。

我設置了一個示例,其中https://www.google.com在 WebView 中呈現。 當用戶發送搜索查詢時,將調用協調器,該協調器調用 UIViewRepresentable 的 function:

視圖- 這是應該通過顯示另一個視圖(通過NavigationLinks實現)對 model 更改做出反應的視圖

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

UIViewControllerRepresentable - 這是在 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()
    }
}

UIViewController - WKNavigationDelegate 對點擊“Google 搜索”做出反應並調用協調器

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()
    }
}

Coordinator - WKNavigationDelegateUIViewRepresentable之間的橋梁

import Foundation
import WebKit

class Coordinator: NSObject {
    var webview: Webview

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

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

更新

我現在有一個帶有 model ( @ObservedObject ) 的視圖。 這個 model 被提供給UIViewControllerRepresentable 當用戶點擊“谷歌搜索”時, UIViewControllerRepresentable成功調用model.startCamera() 但是,model 的這種變化並沒有反映在WebviewContainer中。 這是為什么? 這不是@ObservedObject的全部目的嗎?

我在提供的代碼中添加了Model ,該代碼在startCamera() function 時更新。 @Published變量應該在 UI 線程上更新,因為在大多數情況下,它們會更改 UI state,從而導致 UI 更新。

這是完整的示例:

import SwiftUI
import Foundation
import WebKit

class Coordinator: NSObject {
    var webview: Webview

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

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

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()
    }
}

class WebviewModel: ObservableObject {
    @Published var loggedOut: Bool = false
    @Published var shouldRedirectToCameraView: Bool = false
    @Published var navbarHidden: Bool = false
    func startCamera() {
        print("Started Camera")
        DispatchQueue.main.async {
            self.shouldRedirectToCameraView.toggle()
        }
    }
}

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()
    }
}

struct LoginView: View {
    var body: some View {
        Text("Login")
    }
}

struct CameraView: View {
    @ObservedObject var model: WebviewModel
    var body: some View {
        Text("CameraView")
    }
}

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("Hallo"))
                .navigationBarHidden(self.model.navbarHidden)

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


struct ContentView: View {

    var body: some View {
        WebviewContainer()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

演示

我希望這有幫助。

好的,我在進行了非常激烈的調試 session 之后發現了它:

1.) 我在這篇文章中提供的代碼確實有效。 這只是在我周圍的代碼的上下文中存在問題

2.)到目前為止唯一提供的答案沒有解決任何問題 僅僅因為它已經在工作並且與不在主線程上執行的代碼無關(盡管我當然同意,這應該針對影響 UI 的操作執行)。

3.)在我的情況下,問題出在導致WebviewContainer的視圖中。 在那個視圖中,我有一個 model 在 API 調用中更改其值。 成功時它決定重定向到WebviewContainer以防失敗。 到目前為止,一切都很好。 但是,我在if中進行了 model 更改,並且應該在else中阻止它。 我錯過了其他,所以有一瞬間它正在做正確的事情,然后又切換回來。 這很難調試,因為當我觀看 model 時,一切都已找到。 唯一奇怪的是 model 的構造函數被調用了兩次。

很抱歉,在這種情況下,我無法為給定的答案提供賞金(您將獲得對所投入時間的支持。

非常感謝:學習。 下次嘗試盡可能多地隔離問題,以減少我的應用程序代碼的 rest 的副作用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM