1. WKWebView
1.1 H5调Native(H5向Native传递消息)
1.1.1 H5调Native_直接拦截URL方式
顾名思义就是在WebView加载URL的时候,读出URL字符串,看看里面有没有你和H5特别约定的标记,如果有说明H5想向你传递消息,就干H5让你干的事,忽略这个URL的定向跳转。
拦截方法在下面回调:
webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)
H5那边的代码:
就是重定向当前页面URL。就是改变了location.href
的值。这里举例子标记是bridge://doingSomething
,传参{parma1=sss,parma2=dy}
h5AskNativeDoSomething1(){
location.href = "bridge://doingSomething&parma1=sss&parma2=dy"
}
Native代码:
发现URL中包含bridge://doingSomething
,可以判断H5要让Native干事情,那么就decisionHandler(.cancel)
忽略这个URL的跳转,取出对应参数干事情
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
let changeURLString:String = navigationAction.request.url?.absoluteString ?? "未知"
print("要跳转的URL:\(changeURLString)")
/***方法1 拦截关键标识 bridge://doingSomething,根据自己和H5定了规则,去判断取相应的参数 ***/
//方法1:js调用native
if changeURLString.contains("bridge://doingSomething"){
decisionHandler(.cancel)
let list:[String] = changeURLString.split(separator: "&").compactMap { "\($0)" }
print(list)
//TODO
}else{
decisionHandler(.allow)
}
}
1.1.2 H5调Native_WKScriptMessageHandler 回调方式
这是系统JavaScriptCore
提供的功能,用的时候需要引用类库import JavaScriptCore
。
H5那边代码:
Native那边通过WKScriptMessageHandler注册的js对象暴露出来,这边想向原生传递消息,固定写法:window.webkit.messageHandlers.(JS对象XXXX字符串).postMessage({})
其中 window.webkit.messageHandlers 都是固定写法,.postMessage({})也是固定写法。这里例子完整写法就是
window.webkit.messageHandlers.DIYJSBridge.postMessage({})
记住,如果没有参数传递,({})小括号里面的大括号也不能省略,如果省略{},native那边会无响应!如果有参数传递,那么{}里面就是键值对,如:{"param1":1,"param2":2}
/**
* native那边通过WKScriptMessageHandler注册的js对象暴露出来,这边想向原生传递消息,固定写法:window.webkit.messageHandlers.原生那边暴露的js对象XXXX.postMessage({})
* 其中 window.webkit.messageHandlers 都是固定写法,.postMessage({})也是固定写法。记住,如果没有参数传递,({})小括号里面的大括号也不能省略,如果省略{},native那边会无响应
*/
h5AskNativeDoSomething2(){
window.webkit.messageHandlers.DIYJSBridge.postMessage({})
}
Navtive代码:
我们在WKWebView初始化的时候,给它配置WKWebViewConfiguration
的里面添加一个类似监听的东西:
let configuration:WKWebViewConfiguration = WKWebViewConfiguration()
//加监听
let wkUserContnetController:WKUserContentController = WKUserContentController.init()
wkUserContnetController.add(self, name: "DIYJSBridge")//加监听
configuration.userContentController = wkUserContnetController
webView = WKWebView.init(frame:self.view.bounds, configuration:configuration)
webView.scrollView.decelerationRate = UIScrollView.DecelerationRate.normal
webView.navigationDelegate = self
self.view.addSubview(webView)
wkUserContnetController.add(self, name: "DIYJSBridge")
这里给self
添加一个监听,监听到DIYJSBridge
这个JS对象发送的消息(先这么理解)。
然后实现WKScriptMessageHandler
协议的方法:
//MARK: - WKScriptMessageHandler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("方法2:\(message.name)")
print("方法2:\(message.body)")
}
这里面可以根据WKScriptMessage 读到方法的名称name和参数body,然后就可以干事情。
1.1.3 H5调Native_三方库 WebViewJavascriptBridge
这个库本质还是拦截URL,只是进行了封装,让方法定义和传参更加对象化。具体源码,有机会再写篇文章解析一下!
H5那边的代码:
H5那边需要在index.html定义一个方法,并调用。挂载对应的JS对象,完成相应的配置初始化。
//初始化方法
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'https://__bridge_loaded__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() { document.documentElement.removeChild(WVJBIframe); }, 0);
}
//调用完成配置初始化
setupWebViewJavascriptBridge((bridge) => {});
现在H5想调用Native方法
h5AskNativeDoSomething3(){
this.toast("方法3:H5调用Native")
window.WebViewJavascriptBridge.callHandler('NativeToDingSomething',{"Pa1":"11111"}, function responseCallback(responseData) {
console.log(responseData)
})
}
window.WebViewJavascriptBridge.callHandler()
固定写法,其中参数
第一个参数:Native那边定义标记字符串,如:`NativeToDingSomething`
第二个参数:传参,为{}括起来的键值对,如:`{"Pa1":"11111"}`
第三个参数:回调函数,如果Native那边实现(下面Native实现了)了对应的回调Block,所带的回调参数会在这个responseData里面
Native代码:
Native这边引入了三方库WebViewJavascriptBridge,如后导入。
代码初始化一个WebViewJavascriptBridge实例,并关联上WebView
/**方法3 通过三方类库**/
self.webViewBridge = WebViewJavascriptBridge.init(webView)
self.webViewBridge.setWebViewDelegate(self)
这时设置注册一个标记NativeToDingSomething
,发现H5 callHandler
这个标记后干事情....
self.webViewBridge.registerHandler("NativeToDingSomething") { (ret:Any?, callBack:WVJBResponseCallback?) in
print(ret)
//TODO
if callBack != nil{
callBack!(["result":true,"data":["One","Two","Three"]])
}
}
这样H5那边如果触发了
window.WebViewJavascriptBridge.callHandler('NativeToDingSomething',{"Pa1":"11111"}, function responseCallback(responseData) {
console.log("Native传递过来的参数:",responseData)
})
}
Native这边收到消息会打印
Optional({
Pa1 = 11111;
})
同时Native这边执行了回调callBack!(["result":true,"data":["One","Two","Three"]])
H5那边的控制台会打印:
这样就完成了一次H5发起的双向通信!
1.2 Native调H5(Native向H5传递消息)
1.2.1 Native调H5_直接WebView调用evaluateJavaScript方法
open func evaluateJavaScript(_ javaScriptString: String, completionHandler: ((Any?, Error?) -> Void)? = nil)
如:
self.webView.evaluateJavaScript("nativeAskH5DoSomething('小孩')") { (any:Any?, err:Error?) in
if err == nil{
print("调用成功,返回结果\(any)")
}else{
print("调用失败\(err)")
}
}
其中nativeAskH5DoSomething('小孩')
是H5定义的js方法
window.nativeAskH5DoSomething = function(mess){
console.log(mess)
return "我们都是好孩子"
}
注意:这种方法调用的js方法,必须挂载在Web的window对象上,不然会找不到!
evaluateJavaScript去调用h5中的方法之后,这个function要执行完成之后才会回调的app这边,如果这个function中报错了,回调到app这边就会一直报错。所以建议function中的东西可以先延迟执行。
1.2.2 Native调H5_通过 WebViewJavascriptBridge三方库
这个H5调Native反过来相当于H5注册一个标记,Native去调用。
H5代码,注册方法标识
window.WebViewJavascriptBridge.registerHandler("nativeAskH5DoSomething_WebViewJavascriptBridge",(data,callBack) => {
this.toast("原生调用我了")
console.log("原生调用我了")
console.log("传入参数:",data)
callBack({"p1":1,"p2":90})
})
第一个参数data:表示原生传入的参数
第二个参数callBack:是回调方法,执行后可穿参数给原生,告诉执行情况
Native代码 事件触发执行
self.webViewBridge.callHandler("nativeAskH5DoSomething_WebViewJavascriptBridge", data: "10086") { (ret:Any?) in
print("H5传递回来的参数结果:\(ret)")
}
执行结果:
H5打印结果
Native打印结果
H5传递回来的参数结果:Optional({
p1 = 1;
p2 = 90;
})
这样也完成了一次Native发起的双向通信!
2. UIWebView
H5调Native: 1.1.1,1.1.3 和WKWebView相同!
Native调H5: 1.2.1,1.2.2 和WKWebView相同,多了一种方式!
唯一的区别就是在1.1.2上,同时Native调H5多了使用JSContext方式
WKWebView用的是:WKScriptMessageHandler
UIWebView用的是:JSContext
因为现在都用WKWebView了,具体使用参照老铁博客:https://www.jianshu.com/p/88345985fe94