SwiftUI 中使用 WKWebView

1,350 阅读1分钟

使用

  • WebViewManager 管理 wkwebview 可调用方法 比如goback, 需要观察者模式的通过 block回调, 初始化的时候传了block才会 addObserver

@State var title: String = ""
@State var canGoBack: Bool = false
@State var loadingStatus: LoadingStaus = .Wait
@State var estimatedProgress: Double = 0.0
let manager = WebViewManager()

WebView(manager, listenTitle: { title in
    self.title = title
    print("title: \(title)")
}, listenCanGoBack: { canGoBack in
    self.canGoBack = canGoBack
    print("canGoBack: \(canGoBack)")
}, listenLoadingStatus: { loadingStaus in
    self.loadingStatus = loadingStaus
    print("loadingStaus: \(loadingStaus)")
}, listenEstimatedProgress: { estimatedProgress in
    self.estimatedProgress = estimatedProgress
    print("estimatedProgress: \(estimatedProgress)")
})
    .loadUrl("https://www.baidu.com")

代码

import SwiftUI
import WebKit

typealias StringBlock = (String) -> Void
typealias BoolBlock = (Bool) -> Void
typealias WebLoadingStatusBlock = (LoadingStaus) -> Void
typealias DoubleBlock = (Double) -> Void

/// webview 加载状态
enum LoadingStaus: String {
    case Wait = "wait"
    case Finish = "finish"
    case Faile = "faile"
}

class WebViewManager {
    let webView: WKWebView
    
    init(){
        let pres = WKWebpagePreferences()
        pres.allowsContentJavaScript = true
        let config = WKWebViewConfiguration()
        config.defaultWebpagePreferences = pres
        self.webView = WKWebView(frame: .zero, configuration:  config)
    }
    
    func goBack(){
        if(webView.canGoBack){
            webView.goBack()
        }
    }
}


struct WebView: UIViewRepresentable {
    
    let manager: WebViewManager
    
    private var listenTitle: StringBlock?
    private var listenCanGoBack: BoolBlock?
    private var listenLoadingStatus: WebLoadingStatusBlock?
    private var listenEstimatedProgress: DoubleBlock?
    
    init(_ manager: WebViewManager, listenTitle: StringBlock? = nil, listenCanGoBack: BoolBlock? = nil, listenLoadingStatus: WebLoadingStatusBlock? = nil, listenEstimatedProgress: DoubleBlock? = nil){
        self.manager = manager
        
        self.listenTitle = listenTitle
        self.listenCanGoBack = listenCanGoBack
        self.listenLoadingStatus = listenLoadingStatus
        self.listenEstimatedProgress = listenEstimatedProgress
    }
    
    func makeUIView(context: Context) -> WKWebView {
        manager.webView.navigationDelegate = context.coordinator
        return manager.webView
    }
    
    func updateUIView(_ webView: WKWebView, context: Context) {
        
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, WKNavigationDelegate {
        
        let parent: WebView
        
        init(_ parent: WebView) {
            self.parent = parent
            super.init()
            
            if(self.parent.listenTitle != nil){
                self.parent.manager.webView.addObserver(self, forKeyPath: "title", options: .new, context: nil)
            }
            if(self.parent.listenCanGoBack != nil){
                self.parent.manager.webView.addObserver(self, forKeyPath: "canGoBack", options: .new, context: nil)
            }
            if(self.parent.listenEstimatedProgress != nil) {
                self.parent.manager.webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
            }
        }
        
        deinit{
            if(self.parent.listenTitle != nil){
                self.parent.manager.webView.removeObserver(self, forKeyPath: "title")
            }
            if(self.parent.listenCanGoBack != nil){
                self.parent.manager.webView.removeObserver(self, forKeyPath: "canGoBack")
            }
            if(self.parent.listenEstimatedProgress != nil) {
                self.parent.manager.webView.removeObserver(self, forKeyPath: "estimatedProgress")
            }
        }
        
        func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
            if let callback = self.parent.listenLoadingStatus{
                callback(.Wait)
            }
        }
    
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            if let callback = self.parent.listenLoadingStatus{
                callback(.Finish)
            }
        }
    
        func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
            if let callback = self.parent.listenLoadingStatus{
                callback(.Faile)
            }
        }
        
        func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
            // TODO
            if let url = navigationAction.request.url?.absoluteString {
                //url拦截
                if(url.contains("你要拦截的urL")){
                    //处理拦截逻辑
                }
            }
            decisionHandler(.allow)
        }
        
        
        override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
            if(keyPath == "title"){
                if let callback = self.parent.listenTitle {
                    callback(self.parent.manager.webView.title ?? "")
                }
            }
            if(keyPath == "canGoBack"){
                if let callback = self.parent.listenCanGoBack {
                    callback(self.parent.manager.webView.canGoBack)
                }
            }
            if(keyPath == "estimatedProgress"){
                if let callback = self.parent.listenEstimatedProgress {
                    callback(self.parent.manager.webView.estimatedProgress)
                }
            }
        }
    }
    
    func loadUrl(_ urlString: String) -> Self{
        let url = URL.init(string: urlString)
        if let _url = url {
            let request = URLRequest.init(url: _url)
            self.manager.webView.load(request)
        }

        return self
    }
}