问题
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