欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

WKWebView与JS交互

程序员文章站 2024-03-01 10:27:58
...

之前写了UIWebView与JS交互,今天主要介绍iOS中另一种webView——WKWebView与JS交互。

相对于UIWebView与JS交互,WKWebView与JS交互更简单,WKWebView调用JS方法与UIWebView一样,JS调用WKWebView通过类似发通知的方法就可以了。

准备工作:在控制器中添加WKWebView并加载本地HTML

引入WebKit框架

import WebKit

加载

    fileprivate let webView = WKWebView()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        webView.frame = view.frame
        view.addSubview(webView)
        webView.uiDelegate = self
        webView.navigationDelegate = self
        
        let path = Bundle.main.path(forResource: "test", ofType: "html")
        let url = URL(fileURLWithPath: path!)
        let request = URLRequest(url: url)
        webView.load(request)
    }

遵循WKUIDelegateWKNavigationDelegate协议

值得一提的是,WKWebView加载的JS代码中如果有alert、Confirm和prompt,WKWebView是不会显示的,因此需要我们拦截此类消息并用原生方法处理,拦截方法如下:

//拦截alert(警告框)
    func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
        
        completionHandler()
        let alert = UIAlertController(title: "提示标题", message: "\(message)", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "ok", style: .default, handler:nil))
        alert.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: nil))
        present(alert, animated: true, completion: nil)
    }
    
    //拦截Confirm(确认面板)
    func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
        
    }
    
    //拦截prompt(输入框)
    func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
        
    }

1. WKWebView调用JS方法

调用需要传参方法,假设JS有一个方法swiftAndJS需要我们传一张图片

let pic = #imageLiteral(resourceName: "icon_buy")
webView.evaluateJavaScript("swiftAndJS('\(pic)')", completionHandler: nil)

调用无参数方法,假设JS有一个方法swiftAndJS没有参数

webView.evaluateJavaScript("swiftAndJS()", completionHandler: nil)

调用有返回值的方法,object即JS返回的值

webView.evaluateJavaScript("方法名字") { (object, error) in
            
        }

2. JS调用WKWebView方法

我觉得JS调用WKWebView的方式跟iOS中通知很像,约定一个名字,WKWebView监听这个值,当JS用这个名字发送通知时,WKWebView监听到后就可以执行对应代码了。

监听名字需要用到WKUserContentController,它是WKWebView的configuration的一个属性(有点绕)。

2.1定义一个WKUserContentController

fileprivate var conntentController: WKUserContentController?

给其赋值

conntentController = webView.configuration.userContentController

2.2 WKWebView监听约定的名字

我这里把监听的名字取做jsOpenSwiftFunc并对其监听

conntentController?.add(self, name: "jsOpenSwiftFunc")

2.3 遵循WKScriptMessageHandler协议

遵循该协议需要实现userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)方法,当JS发起通知后,会走此回调,开发中可以监听多个通知名字(暂时叫它通知吧),然后在此回调中通过message.name判断需要执行那个方法。当然也可以只监听一个名字,在JS发通知时,可以在每个消息体中加上特定参数告诉iOS端需要执行什么方法,个人习惯第二种。比如下边的例子,我在消息体中加上method参数用于判断执行某函数。

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        
        //转成约定的类型
        let dict = message.body as! Dictionary<String,String>
        let method:String = dict["method"]!
        let param:String = dict["param"]!
        if method=="swiftFunc"{
            print("JS调用了swift方法,参数是: \(param)")
        }
    }

2.4 在JS代码中发起通知调用WKWebView方法

此处jsOpenSwiftFunc必须和之前监听的名字一致,message是传的参数,如果不要参数可以不传。

var message = {
            'method' : 'swiftFunc',
            'param' : '我是参数',
        };
window.webkit.messageHandlers.jsOpenSwiftFunc.postMessage(message);

完整代码:
controller

import UIKit
import WebKit

class ViewController: UIViewController ,WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler{
    
    fileprivate let webView = WKWebView()
    fileprivate var conntentController: WKUserContentController?

    override func viewDidLoad() {
        super.viewDidLoad()
        
        webView.frame = view.frame
        view.addSubview(webView)
        webView.uiDelegate = self
        webView.navigationDelegate = self
        
        let path = Bundle.main.path(forResource: "test", ofType: "html")
        let url = URL(fileURLWithPath: path!)
        let request = URLRequest(url: url)
        webView.load(request)
        
        conntentController = webView.configuration.userContentController
        
        JSOpenSwift()
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            self.openJSFunc()
        }
    }
    
    //拦截alert(警告框)
    func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
        
        completionHandler()
        let alert = UIAlertController(title: "提示标题", message: "\(message)", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "ok", style: .default, handler:nil))
        alert.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: nil))
        present(alert, animated: true, completion: nil)
    }
    
    //拦截Confirm(确认面板)
    func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
        
    }
    
    //拦截prompt(输入框)
    func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
        
    }
    
    
    //MARK: 调用JS方法
    func openJSFunc(){
        
        //有参数
        let pic = #imageLiteral(resourceName: "icon_buy")
        webView.evaluateJavaScript("swiftAndJS('\(pic)')", completionHandler: nil)
        
    }
    
    
    //MARK: 供JS调用方法
    func JSOpenSwift(){
        conntentController?.add(self, name: "jsOpenSwiftFunc")
    }
    
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        
        //转成约定的类型
        let dict = message.body as! Dictionary<String,String>
        let method:String = dict["method"]!
        let param:String = dict["param"]!
        if method=="swiftFunc"{
            print("JS调用了swift方法,参数是: \(param)")
        }
    }

}

HTML

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>首页</title>
    </head>
    
    <script type="text/javascript">
        
        function swiftAndJS(pic){
            alert("Swift调用了有参数的js方法,参数为" + pic);
        }
    
    function openSwiftFunc(){
        
        var message = {
            'method' : 'swiftFunc',
            'param' : '我是参数',
        };
        window.webkit.messageHandlers.jsOpenSwiftFunc.postMessage(message);
    }
    </script>
    
    <body bgcolor="#dddd">
        
        <div style="margin-top: 100px">
            <input type="button" style="width: 300px; height: 50px; font-size: 30px" value="调用swift方法" onclick="openSwiftFunc()">
        </div>
        
    </body>
</html>