iOS开发--iOS与JS交互的几种方式

Demos

ObjcAndJSDemo1
ObjcAndJSDemo2

iOS与JS交互的几种方式

  • JavaScriptCore:iOS7之后出现的,学习成本不高,是适配iOS7的首选。
  • 拦截协议:拦截协议需要双方共同协商为协议规定一套准则,在交互中要遵循该准则。拦截协议不需要引入任何框架,适合多个平台使用。协议可以如此定义:schemes://model/action?{参数1}={数值1}&{参数2}={数值2}&...
  • 第三方框架WebViewJavaScriptBridge:基于拦截协议进行的封装,学习成本相对JavaScriptCore较高,使用不如JavaScriptCore方便。
  • WKWebView:iOS8之后出现的。

iOS7之前,Objective-C调用JavaScript代码

iOS7以前,Objective-C调用JavaScript的方式只有一种,就是通过UIWebView对象的stringByEvaluatingJavaScriptFromString:方法。

  • stringByEvaluatingJavaScriptFromString:方法只能在主线程执行
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
        [self.webView stringByEvaluatingJavaScriptFromString:@"var javascript = 1 + 2"];
});  
  • 通过stringByEvaluatingJavaScriptFromString:方法可以简单地调用系统提供的JavaScript方法
- (void)webViewDidFinishLoad:(UIWebView *)webView{
    NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
    NSLog(@"%@", title);
}

iOS7之前,JavaScript调用Objective-C

  • URL请求拦截。

在Objective-C代码里设置UIWebViewDelegate代理,实现代理方法

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;

解释:该方法可以监听到UIWebView中发出的URL请求,通过与H5协商一个URL通信协议,来拦截指定的URL,做相应的操作,并阻止此链接的跳转。

例子:
html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div style="margin-top: 10px">
        <input type="button" value="callPhone" onclick="callPhone()">
    </div>
</body>

<script>
    // 声明一个名为callPhone的js函数,其会发出一个链接为nativejs://callPhone的请求
    function callPhone() {
        window.location.href = 'nativejs://callPhone';
    }

</script>

</html>

objc代码

/**
 *  在一个网页开始加载一个frame前被调用
 */
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    NSLog(@"shouldStartLoadWithRequest-------");
    
    NSString *urlString = request.URL.absoluteString;
    NSRange range = [urlString rangeOfString:@"nativejs://"];
    if (range.location != NSNotFound) { // 拦截URL协议头是nativejs的链接
        NSLog(@"执行原生调用相机的方法");
        return NO;// 阻止此链接的跳转
    }
    return YES;
}
  • 监听Cookie。

详见参考链接的原生与H5的交互一文。

iOS7之后,JavaScriptCore的引入,使得Objective-C与JavaScript的交互更为容易

JavaScriptCore中常见的几种类型

  • JSContext:代表JS的执行环境,通过evaluateScript:方法就可以执行JS方法。
  • JSValue:封装了JS与ObjC中对应的模型,以及调用JS的API等。
  • JSExport:一个协议,通过遵守此协议,可以定义我们自己的协议,在协议中声明的API都会在JS中暴露出来,能被JS调用。
  • JSManagedValue:管理数据和方法的类。
  • JSVirtualMachine:处理线程相关,使用较少。

Objective-C调用JavaScript

/**
 *  网页加载完毕时被调用
 */
- (void)webViewDidFinishLoad:(UIWebView *)webView{
    // 获取当前JS执行环境
    JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    NSString *alertJS = @"alert('Hello JS!')"; //准备执行的JS代码
    // 通过evaluateScript:方法调用JS的alert
    [context evaluateScript:alertJS];
}

JavaScript 调用 Objective-C

  • 直接调用JS(不适用于实战项目中)。

    例子1:
    objc代码

    // 获取当前JS执行环境
      JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
      
      // context直接执行JS代码。
      [context evaluateScript:@"var num = 10"];
      [context evaluateScript:@"var squareFunc = function(value) { return value * value }"];
      // 计算正方形的面积
      JSValue *squareArea = [context evaluateScript:@"squareFunc(num)"];
      NSLog(@"squareArea:%@", squareArea.toNumber);
      
      // 也可以通过下标的方式获取到JS函数
      JSValue *squareFunc = context[@"squareFunc"];
      JSValue *squareArea2 = [squareFunc callWithArguments:@[@"20"]];
      NSLog(@"squareArea2:%@", squareArea2.toNumber);
    

    例子2:
    html代码

    <div style="margin-top: 10px">
        <input type="button" value="log" onclick="log('测试')">
    </div>
    

    objc代码

/**

  • 网页加载完毕时被调用
    */
  • (void)webViewDidFinishLoad:(UIWebView *)webView{
    // 获取当前JS执行环境
    JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    // 直接调用JS代码
    context[@"log"] = ^(){
    // 取出JS方法的参数
    NSArray *args = [JSContext currentArguments];
    for (id obj in args) {
    NSLog(@"%@",obj); // 打印JS方法接收到的所有参数
    }
    };
    }

    
    
  • 在ObjC中通过JSContext注入模型,然后调用模型的方法。(重要,项目一般用该方式)

第一步:需要声明一个与JS进行交互的协议(NativeApisProtocol),要求该协议遵守JSExport协议。
第二步:新建一个模型(NativeAPIs),要求该模型遵守NativeApisProtocol协议。一般而言,需要在NativeAPIs模型中声明一个JSContext属性,便于与JS交互。
第三步:实现该模型(NativeAPIs),即在NativeAPIs.m文件中实现NativeApisProtocol协议中定义的方法。
第四步:在ViewController.m的-webViewDidFinishLoad:方法中获取当前JS执行环境(self.jsContext),然后将NativeAPIs模型注入到JS执行环境。

注意:NativeApisProtocol协议中定义的方法是在子线程中执行的,如果在所定义的方法中需要修改界面或者跳转之类的,需要通过GCD回主线程操作。

缺陷:通过参考链接的JavaScript和Objective-C交互的那些事(续)可知,在-webViewDidFinishLoad:方法中注入NativeAPIs模型存在一定的问题,因为这时候网页还没加载完,JavaScript若开始调用Objective-C代码(即NativeApisProtocol协议中定义的方法),会出现调用不到方法的问题。
解决方法:在每次创建JSContext环境的时候,都注入NativeAPIs模型到JSContext环境中。更加具体的方法可以参考第三方库UIWebView-TS_JavaScriptContext。通过引入UIWebView+TS_JavaScriptContext,让ViewController遵守TSWebViewDelegate协议,实现代理协议中的方法-webView:didCreateJavaScriptContext:,以此获取JSContext环境。

objc代码

/*--  NativeAPIs.h  ---*/ 

#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
#import <UIKit/UIKit.h>

// 声明与JS交互的协议
@protocol NativeApisProtocol <JSExport> // 遵守JSExport协议

// 调用系统相机
- (void)callCamera;

// 调用系统分享
- (void)share:(NSString *)shareInfo;

// 打开宁波手机阅读
- (void)openNBPhoneReader;

@end

@interface NativeAPIs : NSObject <NativeApisProtocol>

@property(weak, nonatomic) JSContext *jsContext;

@end

/*--------------------------------------------*/

/*--  NativeAPIs.m --*/ 

/*--  省略,具体看Demo源码 --*/

/*--------------------------------------------*/

// ViewController.m

/*-- 省略前面的代码 --*/

#pragma mark - UIWebViewDelegate

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    NSLog(@"网页加载完毕------");
    
    // 获取JS上下文运行环境
    self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    NativeAPIs *nativeAPIs = [[NativeAPIs alloc] init];
    // 将NativeAPIs模型注入JS
    self.jsContext[@"NativeApis"] = nativeAPIs;
    nativeAPIs.jsContext = self.jsContext;
    
    self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
        context.exception = exceptionValue;
        NSLog(@"异常信息: %@", exceptionValue);
    };
}

html代码

<html>

/*-- 省略无关紧要的代码,具体见Demo --*/

<body>
    <div style="margin-top: 10px">
        <input type="button" value="CallCamera" onclick="NativeApis.callCamera()">
    </div>
    <div style="margin-top: 50px">
        <input type="button" value="Share" onclick="callShare()">
    </div>
    <div style="margin-top: 50px">
        <input type="button" value="OpenReader" onclick="NativeApis.openNBPhoneReader()">
    </div>
</body>

<script>
    // 声明一个名为picCallback的函数,其参数为photo
    var picCallback =  function (photo) {
        alert(photo);
    }

    // 声明一个名为callShare的函数
    var callShare = function () {
        var shareInfo = JSON.stringify(
                                      { "title": "objc&js的交互",
                                        "desc": "就是那些事"}
                                      );
        // 调用原生的share方法
        NativeApis.share(shareInfo);
    }

    // 声明一个名为shareCallback的函数
    var shareCallback =  function () {
        alert('success');
    }

</script>

</html>

参考链接

iOS与JS交互实战篇(ObjC版)

UIWebView 与 JS 交互(1):Objective-C 调用 Javascript

原生与H5的交互

Objective-C与JavaScript交互的那些事

JavaScript和Objective-C交互的那些事(续)

JavaScriptCore在实际项目中的使用的坑

UIWebView中Objective-C与JavaScript交互

UIWebView加载本地HTML文件

OC与JS的交互

iOS中JavaScript和OC交互

UIWebView-TS_JavaScriptContext

关于UIWebView的总结

UIWebview 的javascript與ios objective-c互動傳參數

UIWebview 的javascript與ios objective-c互動傳參數(Ⅱ)

js(javascript)与OC(Objective-C)交互

iOS中的HTML交互简说 -- 推荐

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

推荐阅读更多精彩内容