JS 和 OC 的交互的两种方案

一、webView 的两个方法实现 OC 与 JS 的交互

在 iOS7之前主要使用这种方案

//实现 OC 调用 JS 的代码
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
// 与 web 端定好协议,拦截参数 request ,根据参数来执行不同的 OC 代码
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

1、OC 调用 JS 的代码

辅助代码:

#import "ViewController.h"
@interface ViewController ()<UIWebViewDelegate>
@property (nonatomic,strong) UIWebView * webView;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    UIWebView * webView = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    webView.delegate = self;
    self.webView = webView;
    
    NSURL * url  = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"];
    NSURLRequest * urlRequest = [NSURLRequest requestWithURL:url];
    [webView loadRequest:urlRequest];
    [self.view addSubview:webView];
}

在代理方法中实现 OC 执行 JS 代码:

// 实现 OC 执行 JS 代码
- (void)webViewDidFinishLoad:(UIWebView *)webView{
    NSString * alertJs = @"alert('test js OC')";
    [webView stringByEvaluatingJavaScriptFromString:alertJs];
}

2、JS 调用 OC 的代码

test.html代码:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <p></p>
    <div>
        <button onclick="open_camera();">拍照</button>
    </div>
    <p></p>
    <div>
        <button onclick="call();">打电话</button>
    </div>
<script>

function open_camera() {
    // 定好的协议wb://
    window.location.href = 'wb://openCamera';
}
</script>
</body>
</html>

在js 的函数中定好协议,截取协议中的字符串,根据字符串对应的方法执行相应的 OC 代码

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

    NSString * url = request.URL.absoluteString;
    NSRange range = [url rangeOfString:@"wb://"];
    NSUInteger loc = range.location;
    if (loc != NSNotFound) {
        // 方法名
        NSString * method = [url substringFromIndex:loc + range.length];
        
        // 转成 SEL
        SEL sel = NSSelectorFromString(method);
        [self performSelector:sel];
    }
    
    return YES;
}

- (void)openCamera{
    NSLog(@"%s",__func__);
}
i.png

点击拍照打印:

32.png

方案一是比较 low 的方法,项目推荐使用方案二用JavaScriptCore框架。

二、用 JavaScriptCore 框架

iOS7引入了JS框架<JavaScriptCore/JavaScriptCore.h>,给了“纯iOS程序员”一个枯木逢春的契机~学习强大的 JavaScript

导入<JavaScriptCore/JavaScript.h>可以看到包含五个类:JSContextJSValueJSManagedValueJSVirtualMachineJSExport
开发中 JS 和 OC 的交互主要用 JSContext JSValue JSExport

  • JSContext就为JS提供了运行的环境。通- (JSValue *)evaluateScript:(NSString *)script;方法就可以执行一段JavaScript脚本,并且其中的方法、变量等信息都会被存储在其中以便在需要的时候使用。
  • JSValue 在 Objective-C 对象和 JavaScript 对象之间起转换作用

1、OC 执行 JS 代码

思路:
1、建立 context 运行环境
2、context 执行 js代码(执行之后变量,函数都保存在 context 中了)
3、取出 context 中的函数保存在 JSValue 中
4、JSValue 执行 callWithArguments: 方法传递参数

- (void)OCExcuteJS{
    NSString * path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"js"];
    NSString * testScript = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
   
    //1、创建js执行的环境
    JSContext * context = [[JSContext alloc] init];
    
    // 2、context 执行 js 代码后,里面的变量,函数都存在其中,在使用的时候直接用下标调用即可
    [context evaluateScript:testScript];
    
    // 3、取出 context中的 factorial 中的方法
    JSValue *function = context[@"factorial"];
    
    // 4、JSValue 是函数,执行代码
    JSValue *result = [function callWithArguments:@[@10]];
    
    NSLog(@"factorial(10) = %@, 类型转换后的:%d",result,[result toInt32]);
}

结果:


1.png

2、JS 执行 OC 代码

JS 执行 OC 代码有两种方案:

  • block :对应 JS 的函数
  • JSExport协议:对应 JS 的对象
1)block--->JS 函数

思路:
1、创建 JSContext 环境
2、给 JS 增加一个test函数
3、js 代码
4、context 执行 js 代码

- (void)JSExcuteOC_block{
    // 1.创建 JSContext环境
    JSContext * context = [[JSContext alloc] init];
    
    // 2.给 JS 代码增加一个 test 函数
    context[@"test"] = ^(){
        NSArray * args = [JSContext currentArguments];
        for (id obj in args) {
            NSLog(@"obj = %@",obj);
        }
    };
    
    // 3.JS 函数执行
    NSString * jsFuncstr = @"test('参数1','参数2')";
    // 4.执行 JS代码
    [context evaluateScript:jsFuncstr];
}

执行结果:


2.png

补充:
1、不要在 Block 中直接使用外面的 JSValue 对象, 把 JSValue 当做参数来传进 Block 中。
2、避免循引用,不要在 Block 中直接引用使用外面的 JSContext 对象,应该用[JSContext currentContext];

2)JSExport---->JS对象

只要是添加了JSExport协议的协议,所规定的方法,变量等就会对js开放,我们可以通过js调用到。

遵循 JSExport 的类
#person.h 文件

#import <Foundation/Foundation.h>
@import JavaScriptCore;
@protocol PersonProtocol <JSExport>

//此处我们测试几种参数的情况
-(void)TestNOParameter;
-(void)TestOneParameter:(NSString *)message;
-(void)TestTowParameter:(NSString *)message1 SecondParameter:(NSString *)message2;
@end


@interface Person : NSObject<PersonProtocol>

@end


#person.m文件

#import "Person.h"
@implementation Person
//一下方法都是只是打了个log 等会看log 以及参数能对上就说明js调用了此处的iOS 原生方法
-(void)TestNOParameter
{
    NSLog(@"this is ios TestNOParameter");
}
-(void)TestOneParameter:(NSString *)message
{
    NSLog(@"this is ios TestOneParameter=%@",message);
}
-(void)TestTowParameter:(NSString *)message1 SecondParameter:(NSString *)message2
{
    NSLog(@"this is ios TestTowParameter=%@  Second=%@",message1,message2);
}
@end
执行
    // 1、创建 OC遵循 JSExport的对象
    Person * person = [[Person alloc] init];
    
    // 2、创建 context 环境
    JSContext * context = [[JSContext alloc] init];
    
    // 3、为 JS 添加一个对象
    context[@"person"] = person;
    
    // 4、执行 OC 中对应的方法
    NSString *jsStr1=@"person.TestNOParameter()";
    [context evaluateScript:jsStr1];
    NSString *jsStr2=@"person.TestOneParameter('参数1')";
    [context evaluateScript:jsStr2];
    NSString *jsStr3=@"person.TestTowParameterSecondParameter('参数A','参数B')";
    [context evaluateScript:jsStr3];

结果:

js-oc.png

参考:
http://blog.csdn.net/lwjok2007/article/details/47058795
http://blog.csdn.net/lizhongfu2013/article/details/9232129

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

推荐阅读更多精彩内容