参考:
WK 与 JS 的那些事 WKWebView使用
H5 与 Native 交互之 JSBridge 技术
1.简介
从iOS12开始,苹果正式废弃UIWebView,使用WKWebView。使用WebView的主要问题是原生OC如何与webView通讯的问题,即 js与oc的如何交互。
UIWebView与WKWebView比较:
1.内存占用,加载速度wkwebview有优势.
2.wkwebview将协议和类拆分,更清晰.
3.wkwebview带有进度属性estimatedProgress.
4.wkwebview不支持页面缓存,需要自己注入cookie,而uiwebview是自动注入cookie.
5.wkwebview无法使用jscontext.
2. 原理
1.使用原生调用js
其实就是调用了window下的一个对象,如果我们要让native来调用我们js写的方法,那这个方法就要在window下能访问到.
2.使用js调用原生oc
在webView内发起网络请求都可以被原生拦截.
我们发起一个自定义的网络请求,通常格式: jsbridge://methodName?param1=value1¶m2=value2. 发现是jsbridge://开头的地址,就不进行内容的加载,转而执行相应的调用逻辑
发起这样一个网络请求有两种方式:1. 通过localtion.href;2. 通过iframe方式. 我们通过设置一个隐藏的ifrmame来达到发送自定义请求的目的.
3.交互方式:
使用WKWebview时,原生oc与js交互的主要思想是拦截。有一下几种方式:
- 直接拦截js的url跳转。
- 使用messageHandle。
- 使用三方如WebViewJavaScriptBridge
3.直接拦截js的响应方法。
1.js告诉oc要跳转
WKWebView实现WKNavigationDelegate,当js跳转的时候,代理会实现
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
//根据navigationAction.request 可以拦截每一个js执行的超链接。
decisionHandler(WKNavigationActionPolicyAllow);
}
顺便说一下,WKNavigationDelegate是webView页面跳转时候的代理:
#pragma mark - WKNavigationDelegate
//请求之前,决定是否要跳转:用户点击网页上的链接,需要打开新页面时,将先调用这个方法。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
//接收到相应数据后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
//页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
// 主机地址被重定向时调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation;
// 页面加载完毕时调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation;
//跳转失败时调用
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
// 如果需要证书验证,与使用AFN进行HTTPS证书验证是一样的
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler;
//9.0才能使用,web内容处理中断时会触发
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0);
2.OC告诉js执行方法
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler
//webview 调用该方法使js执行 javaScriptString的代码。
4. 使用messageHandler
1.js调用oc(js给oc发消息)
1.实现方式
WKWebview原生通过实现userContentController类添加MessageHandler实现。
- 设置userContentController的代理
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"JSMessageToOC"];
- 2.在js中,当需要给oc发消息的时候执行如下,这样是为了和安卓样式统一:
window.webkit.messageHandlers.JSMessageToOC.postMessage("js发给oc的一条消息:aaa");
- self 遵守WKScriptMessageHandler协议, 实现方法。当js给oc发送消息的时候,该协议方法会被执行:
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
//通过message.name message.body等获取消息。
}
2.注意循环引用问题:
self引用webView。
webView引用config。
config引用contentController。
contentController添加messageHandler为self来引用self。
打破方式: messageHandler的对象使用代理。使用proxy等
4.使用WebViewJavaScriptBridge.
1.首先需要在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 = 'wvjbscheme://__BRIDGE_LOADED__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}
2.js调用oc
js给oc发消息方式如下:
1.js中执行如下方法,将数据传给oc:
WebViewJavascriptBridge.callHandler('jsCallsOC', {'argument': '18'}, function(response) {
})
2.oc执行如下方法,获取数据.
- data中是js传给oc的消息,
- oc还可以通过调用responseCallback 回应js.
[self.bridge registerHandler:@"jsCallsOC" handler:^(id data, WVJBResponseCallback responseCallback) {
//js调用oc方法,oc执行后,可以调用responseCallback来通知js,oc执行完成了.
NSLog(@"%@---%@",data,responseCallback);
}];
- oc调用js方法,oc发消息给js
- oc中执行如下方法
[self.wjb callHandler:@"OCCallJSFunction" data:@"data" responseCallback:^(id responseData) {
//oc调用js方法, js执行完成后,会回应oc 执行后的responseData.
NSLog(@"%@",responseData);
}];
- 在js中这样写:
oc调用js,给js发消息时, js中方法会执行.
data是oc给js的数据.
js执行完成后,可以调用responsecallback来回应oc.
//所有oc调用的js方法都需要在setup方法中注册.
setupWebViewJavascriptBridge(function(bridge) {
// JS 被调用的方法 OCCallJSFunction 定义的标识
bridge.registerHandler('OCCallJSFunction', function(data, responseCallback) {
alert('JS方法被调用:'+data);
responseCallback('js执行过了');
})
})
网友评论