iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge

上一篇文章介绍了UIWebView 如何通过WebViewJavascriptBridge 来实现JS 与OC 的互相调用,这一篇来介绍一下WKWebView 又是如何通过WebViewJavascriptBridge 来实现JS 与OC 的互相调用的。WKWebView 下使用WebViewJavascriptBridge与UIWebView 大同小异。主要是示例化的类不一样,一些与webView 相关的API调用不一样罢了。?

注意:目前我Demo中的WebViewJavascriptBridge库不是最新版本,在最新的iOS系统有崩溃,各位在使用该第三方库时,要先更新到最新版本。

WKWebView 下使用WebViewJavascriptBridge来实现JS 与OC 的互相调用,也是通过拦截URL来实现的。?

下面开始介绍WKWebView 如何通过WebViewJavascriptBridge 来实现JS 与OC 的互相调用。?

关于下载WebViewJavascriptBridge,然后导入工程的部分就不再赘述了。

第一步,创建WKWebView。

这一步,唯一需要注意的地方,就是不用再设置WKWebView?的navigationDelegate,下一步你就知道为什么了。

- (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];

}

第二步,创建WebViewJavascriptBridge实例。

这里与上一篇文章有一些不同,WKWebView 使用的是WKWebViewJavascriptBridge,而UIWebView 使用的是WebViewJavascriptBridge。

_webViewBridge = [WKWebViewJavascriptBridge bridgeForWebView:self.webView];// 如果控制器里需要监听WKWebView 的`navigationDelegate`方法,就需要添加下面这行。

[_webViewBridge setWebViewDelegate:self];

上一步说了不用再设置WKWebView?的navigationDelegate,那是因为在{-bridgeForWebView:}内已经将WKWebView?的navigationDelegate设置为WKWebViewJavascriptBridge的实例了。

+ (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 功能

为了便于维护,我将所有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 实现。

第四步,在HTML添加关键的js

HMTL 里在调用Native 功能之前,要先添加一个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)

}

如果你看过iOS下JS与OC互相调用(一)–UIWebView 拦截URL,你就会发现这个方法与loadURL很像。?

然后在js 中要主动调用一次上述的setupWebViewJavascriptBridge。

setupWebViewJavascriptBridge(function(bridge){ // 这里注册Native 要调用的js 功能。 bridge.registerHandler('testJSFunction', function(data, responseCallback){ alert('JS方法被调用:'+data);

? ? ? ? responseCallback('js执行过了');

? ? })

? ? // 如果要有其他Native 调用的js 功能,在这里按照上面的格式添加。})

主动调用setupWebViewJavascriptBridge有两个目的:?

1、执行一次wvjbscheme://__BRIDGE_LOADED__请求。?

2、注册Native 要调用的js 功能。

执行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 功能。

讲完过程,终于到了 js 调用Native 的用法了。其实非常的简单,例如我想要利用Native 获取定位信息,那么在HTML中添加一个按钮,onclick事件是locationClick(),按照如下实现即可。

functionlocationClick(){ WebViewJavascriptBridge.callHandler('locationClick',null,function(response){ alert(response);

? ? ? ? document.getElementById("returnValue").value = response;

? ? });

}

Native 执行完代码,将获取到的定位信息,通过callHandler 的第三方参数,回调返回到js 中。?

response 可以是单个值,也可以是数组、键值对等。?

当然如果我们调用Native 的时候,没有参数或者不需要Native 返回信息到js 中。我们还可以这样写:

// 没有参数,有回调可以这样写functionlocationClick(){ WebViewJavascriptBridge.callHandler('locationClick',function(response){ alert(response);

? ? ? ? document.getElementById("returnValue").value = response;

? ? });

}// 没有参数,又不需要回调可以这样写functionshake(){? ? WebViewJavascriptBridge.callHandler('shakeClick');

}

至此,JS 通过WebViewJavascriptBridge调用Native 的功能就完成了。

第六步,Native 调用 JS 功能。

Native 调用js 功能与 js 调用Native 的原理和流程一样。?

1、现在js 中注册,Native 要调用的功能。?

2、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:^(idresponseData){NSLog(@"调用完JS后的回调:%@",responseData);? ? }];}

WKWebView 通过WebViewJavascriptBridge实现js 与Native 的交互,到这里就已经完成了。?

这是工程示例中的效果图:

示例工程地址:JS_OC_WebViewJavascriptBridge

Have Fun!

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/u011619283/article/details/52352514

?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容