WKWebView与JS交互
之前写了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)
}
遵循WKUIDelegate
和WKNavigationDelegate
协议
值得一提的是,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>