一策泣、WKWebView
WKWebView 初始化時俺猿,有一個參數(shù)叫configuration,它是WKWebViewConfiguration類型的參數(shù),而WKWebViewConfiguration有一個屬性叫userContentController啸罢,它又是WKUserContentController類型的參數(shù)。
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.preferences = [[WKPreferences alloc] init];
config.preferences.minimumFontSize = 10;
config.preferences.javaScriptEnabled = YES;
config.preferences.javaScriptCanOpenWindowsAutomatically = NO;
config.userContentController = [[WKUserContentController alloc] init];
config.processPool = [[WKProcessPool alloc] init];
config.userContentController = [WKUserContentController new]; //在創(chuàng)建wkWebView時胎食,需要將被js調(diào)用的方法注冊進去,oc與js端對應(yīng)實現(xiàn)
[config.userContentController addScriptMessageHandler:self name:@"callFunciton"];
WKWebView *wkWebView = [[WKWebView alloc]initWithFrame:self.view.frame configuration:config];
self.wkWebView = wkWebView;
wkWebView.navigationDelegate = self;
wkWebView.UIDelegate = self;
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:self.url];
[wkWebView loadRequest:request];
[self.view addSubview:wkWebView];</pre>
1.JS調(diào)用原生MessageHandler
WKUserContentController對象有一個方法
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
JS調(diào)用OC時扰才,這句代碼非常重要
// 在創(chuàng)建wkWebView時,需要將被js調(diào)用的方法注冊進去,oc與js端對應(yīng)實現(xiàn)
[self.wkWebView.configuration.userContentControlle addScriptMessageHandler:self name:@"callFunciton"];
addScriptMessageHandler:name:有兩個參數(shù)厕怜,第一個參數(shù)是userContentController的代理對象衩匣,第二個參數(shù)是JS里發(fā)送postMessage的對象。
所以要使用MessageHandler功能粥航,就必須要實現(xiàn)WKScriptMessageHandler協(xié)議琅捏。
1.1.實現(xiàn)WKScriptMessageHandler代理方法
當js調(diào)用callFunction方法時,會回調(diào)此代理方法:
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ if ([message.name isEqualToString:@"callFunction"]) {
NSString *methodName = message.name;
id params = message.body;
//調(diào)用原生掃碼
if ([methodName isEqualToString:@"scan"]) {
}
}
}
Tip: addScriptMessageHandler很容易引起循環(huán)引用躁锡,導(dǎo)致控制器無法被釋放
- (void)dealloc{
[self.wkWebView.configuration.userContentController removeScriptMessageHandlerForName:@"callFunction"];
}
1.2.JS中使用方法:
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
//其中<name>午绳,就是上面方法里的第二個參數(shù)`name`置侍。
//例如我們調(diào)用API的時候第二個參數(shù)填@"callFunction"映之,那么在JS里就是:
window.webkit.messageHandlers.callFunction.postMessage(<messageBody>)
//<messageBody>是一個鍵值對,鍵是body蜡坊,值可以有多種類型的參數(shù),body 的類型:Allowed types are NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull messageBody可以為NULL或者其他參數(shù)杠输,不能什么都不寫,否則不走代理方法
2.原生調(diào)用JS
//1無參數(shù)
[self.webView evaluateJavaScript:@"show()" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
//TODO
}];
//2有參數(shù)
NSDictionary *dict = @{ @"userAgent": @“userAgentM”, @"custMac":@“custMac”};
NSString *callBackString = [NSString stringWithFormat:@"show(%@)",[self jsonToString:dict]];
[self.webView evaluateJavaScript:callBackString completionHandler:^(id _Nullable obj, NSError * _Nullable error) {
}];
3.WKNavigationDelegate
可以在此通過連接的方式傳遞一些簡單的參數(shù)秕衙,也是一種H5與原生交互
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString *url = navigationAction.request.URL.absoluteString;
if(![url isEqualToString:self.strURL]) {
// 頁面跳轉(zhuǎn)
}
decisionHandler(WKNavigationActionPolicyAllow);
}
二蠢甲、WebViewJavaScriptBridge
WebViewJavaScriptBridge 用于 WKWebView & UIWebView 中 OC 和 JS 交互。
它的基本原理是: 把 OC 的方法注冊到橋梁中据忘,讓 JS 去調(diào)用鹦牛;把 JS 的方法注冊在橋梁中,讓 OC 去調(diào)用勇吊。
1. 初始化
1.導(dǎo)入頭文件 #import <WebViewJavascriptBridge.h>
2.建立 WebViewJavaScriptBridge 和 WebView 之間的關(guān)系
_jsBridge = [WebViewJavascriptBridge bridgeForWebView:_webView];
3.在HTML 文件中曼追,復(fù)制粘貼這兩段 JS 函數(shù)
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
return callback(WebViewJavascriptBridge);
}if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback);
}
window.WVJBCallbacks = [callback]; // 創(chuàng)建一個 WVJBCallbacks 全局屬性數(shù)組,并將 callback 插入到數(shù)組中汉规。
var WVJBIframe = document.createElement('iframe'); // 創(chuàng)建一個 iframe 元素
WVJBIframe.style.display = 'none'; // 不顯示 WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__'; // 設(shè)置 iframe 的 src 屬性
document.documentElement.appendChild(WVJBIframe); // 把 iframe 添加到當前文導(dǎo)航上礼殊。
setTimeout(function() {
document.documentElement.removeChild(WVJBIframe)
}, 0)
}
// 這里主要是注冊O(shè)C將要調(diào)用的JS方法。
setupWebViewJavascriptBridge(function(bridge){
});
2. 注入OC针史、JS方法
往橋梁中注入 OC 方法
/* scanClick 是 OC block 的一個別名
* block本身晶伦,是JS通過某種方式調(diào)用到scanClick的時候,執(zhí)行的代碼塊
* data,由于OC這端由JS調(diào)用啄枕,所以data是JS端傳遞過來的數(shù)據(jù)
* responseCallback OC端的block 執(zhí)行完畢之后,往JS端傳遞的數(shù)據(jù)
*/
[_jsBridge registerHandler:@"scanClick" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"dataFrom JS : %@",data[@"data"]);
responseCallback(@"掃描結(jié)果 : www.baidu.com");
}];
往橋梁中注入 JS 函數(shù)
/* testJavaScriptFunction: 是注入到橋梁中JS函數(shù)的別名,以供OC端調(diào)用婚陪。
* data: 回調(diào)函數(shù)的data,既然JS函數(shù)由OC調(diào)用,所以data是OC端傳遞過來的數(shù)據(jù)。
* responseCallback: JS調(diào)用在被OC調(diào)用完畢之后频祝,向OC端傳遞的數(shù)據(jù)
*/
// 這里主要是注冊 OC 將要調(diào)用的 JS 方法近忙。
setupWebViewJavascriptBridge(function(bridge){
// 聲明 OC 需要調(diào)用的 JS 方法竭业。
bridge.registerHanlder('testJavaScriptFunction',function(data,responseCallback){
// data 是 OC 傳遞過來的數(shù)據(jù).
// responseCallback 是 JS 調(diào)用完畢之后傳遞給 OC 的數(shù)據(jù)
alert("JS 被 OC 調(diào)用了.");
responseCallback({data: "js 的數(shù)據(jù)",from : "JS"});
})
});
3. 調(diào)用OC、JS方法
OC調(diào)用JS
// 單純的調(diào)用 JSFunction及舍,不往 JS 傳遞參數(shù)未辆,也不需要 JSFunction 的返回值。
[_jsBridge callHandler:@"changeBGColor"];
// 調(diào)用 JSFunction锯玛,并向 JS 傳遞參數(shù)咐柜,但不需要 JSFunciton 的返回值。
[_jsBridge callHandler:@"changeBGColor" data:@"把 HTML 的背景顏色改成橙色!!!!"];
// 調(diào)用 JSFunction 攘残,并向 JS 傳遞參數(shù)拙友,也需要 JSFunction 的返回值。
[_jsBridge callHandler:@"changeBGColor" data:@"傳遞給 JS 的參數(shù)" responseCallback:^(id responseData) {
NSLog(@"JS 的返回值: %@",responseData);
}];
JS調(diào)用OC
// JS單純的調(diào)用OC的block
WebViewJavascriptBridge.callHandler('scanClick');
// JS調(diào)用OC的block,并傳遞JS參數(shù)
WebViewJavascriptBridge.callHandler('scanClick',"JS 參數(shù)");
// JS調(diào)用OC的block,傳遞JS參數(shù),并接受OC的返回值歼郭。
WebViewJavascriptBridge.callHandler('scanClick',{data : "這是JS傳遞到OC的掃描數(shù)據(jù)"},function(dataFromOC){
alert("JS 調(diào)用了 OC 的掃描方法!");
document.getElementById("returnValue").value = dataFromOC;
});
4. OC釋放Block
OC中遗契,在當前控制器消失的時候,要記得把注入到橋梁中的 OC block病曾,從橋梁中刪除牍蜂,否則,可能會出現(xiàn)控制器無法釋放的情況泰涂。
[_jsBridge removeHandler:@"scanClick"];
5.示例
1.JS -> OC 的交互
在 OC 中鲫竞,通過 WebViewJavascriptBridge 注冊一個修改 navigationBar 顏色的 Block
[_jsBridge registerHandler:@"colorClick" handler:^(id data, WVJBResponseCallback responseCallback) {
self.navigationController.navigationBar.barTintColor = [UIColor colorWithRed:arc4random_uniform(256) / 255.0 green:arc4random_uniform(256) / 255.0 blue:arc4random_uniform(256) / 255.0 alpha:1.0];
responseCallback(@"顏色修改完畢!");
}];
在 JS 中,通過某種方式去調(diào)用這個 OC 的 block逼蒙。
WebViewJavascriptBridge.callHandler('colorClick',function(dataFromOC) {
alert("JS 調(diào)用了 OC 注冊的 colorClick 方法");
document.getElementById("returnValue").value = dataFromOC;
})
OC -> JS 的交互
往橋梁中从绘,注入一個修改 HTML body 顏色的 JSFunction。
// 在這里聲明OC需要主動調(diào)用JS的方法是牢。
setupWebViewJavascriptBridge(function(bridge) {
bridge.registerHandler('changeBGColor',function(data,responseCallback){
// alert('aaaaaa');
document.body.style.backgroundColor = "orange";
document.getElementById("returnValue").value = data;
});
});
然后在 OC 端通過橋梁調(diào)用這個 changeBGColor
僵井。
[_jsBridge callHandler:@"changeBGColor" data:@"把 HTML 的背景顏色改成橙色!!!!"];
轉(zhuǎn)載: iOS原生與H5交互
參考: WebViewJavaScriptBridge 基本使用