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

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

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

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

第一步,创建WKWebView。

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

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

上一步说了不用再设置WKWebViewnavigationDelegate,那是因为在{-bridgeForWebView:}内已经将WKWebViewnavigationDelegate设置为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(),按照如下实现即可。

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

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

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

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

示例工程地址:JS_OC_WebViewJavascriptBridge

Have Fun!

iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge的更多相关文章

  1. iOS下JS与OC互相调用(五)--UIWebView + WebViewJavascriptBridge

    WebViewJavascriptBridge是一个有点年代的JS与OC交互的库,使用该库的著名应用还挺多的,目前这个库有7000+star.我去翻看了它的第一版本已经是4年前了,在版本V4.1.4以 ...

  2. iOS下JS与OC互相调用(四)--JavaScriptCore

    前面讲完拦截URL的方式实现JS与OC互相调用,终于到JavaScriptCore了.它是从iOS7开始加入的,用 Objective-C 把 WebKit 的 JavaScript 引擎封装了一下, ...

  3. iOS下JS与OC互相调用(一)--UIWebView 拦截URL

    最近准备把之前用UIWebView实现的JS与原生相互调用功能,用WKWebView来替换.顺便搜索整理了一下JS 与OC 交互的方式,非常之多啊.目前我已知的JS 与 OC 交互的处理方式: * 1 ...

  4. iOS下JS与OC互相调用(二)--WKWebView 拦截URL

    在上篇文章中讲述了使用UIWebView拦截URL的方式来处理JS与OC交互. 由于UIWebView比较耗内存,性能上不太好,而苹果在iOS 8中推出了WKWebView. 同样的用WKWebVie ...

  5. iOS下JS与OC互相调用

    背景情况: app项目中有几个界面是需要经常变动的(不仅是内容还有UI布局等),比如活动宣传界面就是属于这一类.但是由于AppStore提交审核也是需要时间的,就算审核快,也不至于每次都为了这点事频繁 ...

  6. iOS下JS与OC互相调用(八)--Cordova详解+实战

    扯两句,可以跳过 由于项目中Cordova相关功能一直是同事在负责,所以也没有仔细的去探究Cordova到底是怎么使用的,又是如何实现JS 与 OC 的交互.所以我基本上是从零开始研究和学习Cordo ...

  7. iOS下JS与OC互相调用(八)--Cordova简单实战

    新建工程,添加Cordova 关键类 新建一个工程TestCordova 然后添加:confug.xml.Private 和 Public 两个文件夹里的所有文件 然后build 发现报错 为什么有会 ...

  8. iOS下JS与OC互相调用(七)--Cordova 基础

    Cordova 简介 在介绍Cordova之前,必须先提一下PhoneGap.PhoneGap 是Nitobi软件公司2008年推出的一个框架,旨在弥补web 和iOS 之间的不足,使得web 和 i ...

  9. iOS下JS与OC互相调用(三)--MessageHandler

    使用WKWebView的时候,如果想要实现JS调用OC方法,除了拦截URL之外,还有一种简单的方式.那就是利用WKWebView的新特性MessageHandler来实现JS调用原生方法. Messa ...

随机推荐

  1. TF-IDF In Scikit-Learn

    TF-IDF In Scikit-Learn 2017年9月30日补充   其实在算下面TF-IDF的步骤之前,还有一步,就是计算Term Frequency 也就是词频.当然,scikit-lear ...

  2. 【NOIP2014TG】solution

    链接:https://www.luogu.org/problem/lists?name=&orderitem=pid&tag=83|31 D1T1(rps) 题意:给你一个周期,以及胜 ...

  3. [bzoj4923]K小值查询

    来自FallDream的博客,未经允许,请勿转载,谢谢. 维护一个长度为n的正整数序列a_1,a_2,...,a_n,支持以下两种操作: 1 k,将序列a从小到大排序,输出a_k的值. 2 k,将所有 ...

  4. Linux查看Tomcat是否多次重启命令

    1.查看命令 ps -ef|grep apache-tomcat-9090|grep -v grep|awk '{print $2}' 如果存在两个端口则多次重启, 2.停掉命令: kill -9  ...

  5. C++变量和基本类型——2.3.1引用

    引用:为对象起了另外一个名字,通常将申明符写成&d的形式来定义引用类型,其中d是申明的变量名: int ival =1024; int &refVal=ival 一般在初始化变量时,初 ...

  6. Python中的文件路径的分隔符

    主要是需要考虑分隔符的问题: 在Windows系统下的分隔符是:\ (反斜杠). 在Linux系统下的分隔符是:/(斜杠). 当在字符中出现\时,大家就要考虑到转义字符了. 转义字符的概念,参考维基百 ...

  7. Triangle(动态规划)

    题目描述 Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjac ...

  8. sqlserver 按照特定值排序查询结果

    select * from t_ss_student order by case when xm like '林%' then 1 else 2 end asc; 姓林的会排在前面

  9. thymeleaf:局部变量 th:with

    当th:with被处理,firstPer变量创建一个局部变量和变量添加到map自上下文,以便它是用于评估和其他上下文中声明的变量从开始,但只有包含< div >标记的范围内. <di ...

  10. Gradle入门--基本配置

    Gradle配置: Gradle构建脚本 build.gradle Gradle属性文件 gradle.properties Gradle设置文件 settings.gradle build.grad ...