SwiftUI WKWebView detect url changing

生来就可爱ヽ(ⅴ<●) 提交于 2020-02-25 04:25:10

问题


I'm a swift learner. I work with SwiftUI which is a struct, I have to implement a WKWebView and in that, a url is changing dynamically. I have to catch these changing urls, but solutions I have tried are not working.

For example: https://stackoverflow.com/a/48273950/10088243 I tried this code block but it is not working and it gives me some compiler errors:

import SwiftUI
import WebKit

struct ContentView: UIViewRepresentable, WKNavigationDelegate {

    let request = URLRequest(url: URL(string: "https://apple.com")!)

    func makeUIView(context: Context) -> WKWebView  {
    let preferences = WKPreferences()
    preferences.javaScriptEnabled = true
    preferences.javaScriptCanOpenWindowsAutomatically = true

    let configuration = WKWebViewConfiguration()
    configuration.preferences = preferences
    let webView = WKWebView(frame: .zero, configuration: configuration)
    webView.allowsBackForwardNavigationGestures = true


    return webView
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 
 // 'override' can only be specified on class membe
  if keyPath == #keyPath(WKWebView.url) {
    print("### URL:", self.webView.url!)
  }

  if keyPath == #keyPath(WKWebView.estimatedProgress) {
    // When page load finishes. Should work on each page reload.
    if (self.webView.estimatedProgress == 1) {
      print("### EP:", self.webView.estimatedProgress)
    }
  }
}

func updateUIView(_ uiView: WKWebView, context: Context) {
    uiView.load(request)
}

func webViewDidFinishLoad(webView : WKWebView) {
    print("Loaded: \(String(describing: webView.url))")
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    print("Loaded: \(String(describing: webView.url))")
    //progressView.isHidden = true
}

func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
    //progressView.isHidden = false
    print("Loaded: \(String(describing: webView.url))")
    }
}

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

I have a Non-class type 'ContentView' cannot conform to class protocol 'NSObjectProtocol' error at line struct ContentView...


回答1:


You can simply create a ObservableObject model class of webview with the name "WebViewModel" like

class WebViewModel: ObservableObject {
    @Published var link: String
    @Published var didFinishLoading: Bool = false

    init (link: String) {
        self.link = link
    }
} 

and also import

import WebKit
import Combine

and then copy this code snippets

struct SwiftUIWebView: UIViewRepresentable {
    @ObservedObject var viewModel: WebViewModel

    let webView = WKWebView()

    func makeUIView(context: UIViewRepresentableContext<SwiftUIWebView>) -> WKWebView {
        self.webView.navigationDelegate = context.coordinator
        if let url = URL(string: viewModel.link) {
            self.webView.load(URLRequest(url: url))
        }
        return self.webView
    }

    func updateUIView(_ uiView: WKWebView, context: UIViewRepresentableContext<SwiftUIWebView>) {
        return
    }

    class Coordinator: NSObject, WKNavigationDelegate {
        private var viewModel: WebViewModel

        init(_ viewModel: WebViewModel) {
            self.viewModel = viewModel
        }

        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            //print("WebView: navigation finished")
            self.viewModel.didFinishLoading = true
        }
    }

    func makeCoordinator() -> SwiftUIWebView.Coordinator {
        Coordinator(viewModel)
    }
}



struct SwiftUIWebView_Previews: PreviewProvider {
    static var previews: some View {

        SwiftUIWebView(viewModel: WebViewModel(link: "https://google.com"))
        //WebView(request: URLRequest(url: URL(string: "https://www.apple.com")!))
    }
}

and in you view

struct AnyView: View {
    @ObservedObject var model = WebViewModel(link: "https://www.wikipedia.org/")
}

var body: some View {


        NavigationView {
       SwiftUIWebView(viewModel: model)
                if model.didFinishLoading {
                    //do your stuff 
                }
   }
   }

so in this way you can get the others delegates response.




回答2:


you use this to delegates of WKNavigationProtocol to perform(e.g to allow or cancel URL Loading) your action

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    if let host = navigationAction.request.url?.host {
        if host.contains("facebook.com") {
            decisionHandler(.cancel)
            return
        }
    }

    decisionHandler(.allow)
}



回答3:


I have found a very good solution to my question. I will post it here. Maybe someone wants to see it and might be useful to them.

observe.observation = uiView.observe(\WKWebView.url, options: .new) { view, change in
    if let url = view.url {
        // do something with your url
    }
}



回答4:


You can use key/value observation to detect changes to the url property of the WKWebView.

Here is a simple example of wrapping a WKWebView in a UIViewRepresentable.

Note that because we are modifying a property, the UIViewRepresentable is a final class rather than a struct.

import Combine
import SwiftUI
import WebKit

final class WebView: UIViewRepresentable {

    @Published var url: URL? = nil {
        didSet {
            if url != nil {
                willChange.send(url)
            }
        }
    }

    private let view = WKWebView()

    private var urlChangedObservation: NSKeyValueObservation?
    private let willChange = PassthroughSubject<URL?, Never>()

    func makeUIView(context: Context) -> WKWebView {
        return makeWebView()
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
    }

    func display(_ html: String) {
        self.view.loadHTMLString(html, baseURL: nil)
    }

    public func load(_ url: String) -> WebView {
        let link = URL(string: url)!
        let request = URLRequest(url: link)
        self.view.load(request)
        return self
    }

    func makeWebView() -> WKWebView {
        self.urlChangedObservation = self.view.observe(\WKWebView.url, options: .new) { view, change in
            if let url = view.url {
                self.url = url
            }
        }
        return self.view
    }
}

You can then listen to the url modified notification in the onReceive() of the container holding the WebView:

.onReceive(self.webview.$url) { url in
                    if let url = url {
                }
}


来源:https://stackoverflow.com/questions/58336457/swiftui-wkwebview-detect-url-changing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!