上一篇文章介紹了UIWebView 如何經過WebViewJavascriptBridge 來實現JS 與OC 的互相調用,這一篇來介紹一下WKWebView 又是如何經過WebViewJavascriptBridge 來實現JS 與OC 的互相調用的。WKWebView 下使用WebViewJavascriptBridge與UIWebView 大同小異。主要是示例化的類不同,一些與webView 相關的API調用不同罷了。 html
WKWebView 下使用WebViewJavascriptBridge
來實現JS 與OC 的互相調用,也是經過攔截URL來實現的。 下面開始介紹WKWebView 如何經過WebViewJavascriptBridge 來實現JS 與OC 的互相調用。 關於下載WebViewJavascriptBridge,而後導入工程的部分就再也不贅述了。java
這一步,惟一須要注意的地方,就是不用再設置WKWebView
的navigationDelegate
,下一步你就知道爲何了。git
- (void)initWKWebView
{
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = [WKUserContentController new];
WKPreferences *preferences = [WKPreferences new];
preferences.javaScriptCanOpenWindowsAutomatically = YES;
preferences.minimumFontSize = 30.0;
configuration.preferences = preferences;
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil];
NSString *localHtml = [NSString stringWithContentsOfFile:urlStr encoding:NSUTF8StringEncoding error:nil];
NSURL *fileURL = [NSURL fileURLWithPath:urlStr];
[self.webView loadHTMLString:localHtml baseURL:fileURL];
self.webView.UIDelegate = self;
[self.view addSubview:self.webView];
}
複製代碼
這裏與上一篇文章有一些不一樣,WKWebView 使用的是WKWebViewJavascriptBridge
,而UIWebView 使用的是WebViewJavascriptBridge
。github
_webViewBridge = [WKWebViewJavascriptBridge bridgeForWebView:self.webView];
// 若是控制器裏須要監聽WKWebView 的`navigationDelegate`方法,就須要添加下面這行。
[_webViewBridge setWebViewDelegate:self];
複製代碼
上一步說了不用再設置WKWebView
的navigationDelegate
,那是由於在{-bridgeForWebView:}
內已經將WKWebView
的navigationDelegate
設置爲WKWebViewJavascriptBridge
的實例了。web
+ (instancetype)bridgeForWebView:(WKWebView*)webView {
WKWebViewJavascriptBridge* bridge = [[self alloc] init];
[bridge _setupInstance:webView];
[bridge reset];
return bridge;
}
- (void) _setupInstance:(WKWebView*)webView {
_webView = webView;
_webView.navigationDelegate = self;
_base = [[WebViewJavascriptBridgeBase alloc] init];
_base.delegate = self;
}
複製代碼
爲了便於維護,我將全部js 要調用Native 功能放在了一個方法裏添加,而後每一個功能再單獨處理。 示例代碼以下:數組
#pragma mark - private method
- (void)registerNativeFunctions
{
[self registScanFunction];
[self registShareFunction];
[self registLocationFunction];
[self regitstBGColorFunction];
[self registPayFunction];
[self registShakeFunction];
}
// 註冊的獲取位置信息的Native 功能
- (void)registLocationFunction
{
[_webViewBridge registerHandler:@"locationClick" handler:^(id data, WVJBResponseCallback responseCallback) {
// 獲取位置信息
NSString *location = @"廣東省深圳市南山區學府路XXXX號";
// 將結果返回給js
responseCallback(location);
}];
}
複製代碼
關於- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler
,咱們能夠這樣理解,後面的block 參數是js 要調用的Native 實現,前面的handlerName 是這個Native 實現的別名。而後js 裏調用handlerName 這個別名,WebViewJavascriptBridge
最終會執行block 裏的Native 實現。bash
HMTL 裏在調用Native 功能以前,要先添加一個js 方法,而後主動調用一次該方法。 要添加的方法是:app
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)
}
複製代碼
若是你看過iOS下JS與OC互相調用(一)--UIWebView 攔截URL,你就會發現這個方法與loadURL
很像。 而後在js 中要主動調用一次上述的setupWebViewJavascriptBridge
。ide
setupWebViewJavascriptBridge(function(bridge) {
// 這裏註冊Native 要調用的js 功能。
bridge.registerHandler('testJSFunction', function(data, responseCallback) {
alert('JS方法被調用:'+data);
responseCallback('js執行過了');
})
// 若是要有其餘Native 調用的js 功能,在這裏按照上面的格式添加。
})
複製代碼
主動調用setupWebViewJavascriptBridge
有兩個目的: 一、執行一次wvjbscheme://__BRIDGE_LOADED__
請求。 二、註冊Native 要調用的js 功能。ui
執行wvjbscheme://__BRIDGE_LOADED__
,而後在WKWebView 的navigationDelegate
方法中攔截該URL ,而後往HMTL中注入js。如下源碼都摘自WebViewJavascriptBridge
:
- (void)webView:(WKWebView *)webView
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
if (webView != _webView) { return; }
NSURL *url = navigationAction.request.URL;
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if ([_base isCorrectProcotocolScheme:url]) {
// 在這裏攔截wvjbscheme://__BRIDGE_LOADED__
if ([_base isBridgeLoadedURL:url]) {
// 這裏會注入js
[_base injectJavascriptFile];
} else if ([_base isQueueMessageURL:url]) {
[self WKFlushMessageQueue];
} else {
[_base logUnkownMessage:url];
}
decisionHandler(WKNavigationActionPolicyCancel);
}
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) {
[_webViewDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler];
} else {
decisionHandler(WKNavigationActionPolicyAllow);
}
}
- (void)injectJavascriptFile {
//讀取js 內容
NSString *js = WebViewJavascriptBridge_js();
// 執行Native 的API,實現將js 注入 到HMTL中。
[self _evaluateJavascript:js];
if (self.startupMessageQueue) {
NSArray* queue = self.startupMessageQueue;
self.startupMessageQueue = nil;
for (id queuedMessage in queue) {
[self _dispatchMessage:queuedMessage];
}
}
}
複製代碼
WKWebView 執行js 的API 與 UIWebView 有些不一樣,WKWebView 用的是{-evaluateJavaScript: completionHandler:}
,這個API 不會馬上返回執行結果,js 的執行結果會在block 中返回。
講完過程,終於到了 js 調用Native 的用法了。其實很是的簡單,例如我想要利用Native 獲取定位信息,那麼在HTML中添加一個按鈕,onclick事件是locationClick(),按照以下實現便可。
function locationClick() {
WebViewJavascriptBridge.callHandler('locationClick',null,function(response) {
alert(response);
document.getElementById("returnValue").value = response;
});
}
複製代碼
Native 執行完代碼,將獲取到的定位信息,經過callHandler 的第三方參數,回調返回到js 中。 response 能夠是單個值,也能夠是數組、鍵值對等。 固然若是咱們調用Native 的時候,沒有參數或者不須要Native 返回信息到js 中。咱們還能夠這樣寫:
// 沒有參數,有回調能夠這樣寫
function locationClick() {
WebViewJavascriptBridge.callHandler('locationClick',function(response) {
alert(response);
document.getElementById("returnValue").value = response;
});
}
// 沒有參數,又不須要回調能夠這樣寫
function shake() {
WebViewJavascriptBridge.callHandler('shakeClick');
}
複製代碼
至此,JS 經過WebViewJavascriptBridge
調用Native 的功能就完成了。
Native 調用js 功能與 js 調用Native 的原理和流程同樣。 一、如今js 中註冊,Native 要調用的功能。 二、Native 調用註冊時,該功能的別名,就能夠完成調用。 在js 中註冊 Native 要調用的功能,一樣須要爲該功能設置一個別名HandlerName。 其實這個步驟在前面介紹過,代碼以下:
setupWebViewJavascriptBridge(function(bridge) {
// 這裏註冊Native 要調用的js 功能。
bridge.registerHandler('testJSFunction', function(data, responseCallback) {
alert('JS方法被調用:'+data);
responseCallback('js執行過了');
})
// 若是要有其餘Native 調用的js 功能,在這裏按照上面的格式添加。
})
複製代碼
上述代碼,是在JS 中註冊了一個別名叫testJSFunction
的js功能,第二個參數是一個function。function裏的data ,就是Native 調用該功能時傳過來的參數,responseCallback是執行完js 代碼後,經過responseCallback將必要的信息返回到Native中。
Native 調用js 裏註冊的功能,示例代碼:
- (void)rightClick
{
// // 若是不須要參數,不須要回調,使用這個
// [_webViewBridge callHandler:@"testJSFunction"];
// // 若是須要參數,不須要回調,使用這個
// [_webViewBridge callHandler:@"testJSFunction" data:@"一個字符串"];
// 若是既須要參數,又須要回調,使用這個
[_webViewBridge callHandler:@"testJSFunction" data:@"一個字符串" responseCallback:^(id responseData) {
NSLog(@"調用完JS後的回調:%@",responseData);
}];
}
複製代碼
WKWebView 經過WebViewJavascriptBridge
實現js 與Native 的交互,到這裏就已經完成了。 這是工程示例中的效果圖:
示例工程地址:JS_OC_WebViewJavascriptBridge
Have Fun!