揭开OC与JS交互调用的神秘面纱

首先OC里要和JS去交互,先行引入头文件#import <JavaScriptCore/JavaScriptCore.h>
同样的,创建一个和屏幕大小的webView用于渲染HTML的页面,

    NSString *path = [[NSBundle mainBundle] bundlePath];
    NSURL *baseURL = [NSURL fileURLWithPath:path];
    NSString * htmlPath = [[NSBundle mainBundle] pathForResource:@"index"
                                                          ofType:@"html"];
    NSString * htmlCont = [NSString stringWithContentsOfFile:htmlPath
                                                    encoding:NSUTF8StringEncoding
                                                       error:nil];
    self.webView.delegate = self;
    [self.webView loadHTMLString: htmlCont baseURL:baseURL];

接下来写了一段蹩脚的HTML代码如下:
index.html

<!DOCTYPE html>
<html lang="en">
   <head>
       <meta charset="UTF-8">
           <title>OC与JS交互</title>
           
           <script type="text/javascript">
               function secondClick(index) {
                   wgb_iOSHookJSButtonClick(index);
               }
           
           function showAlert(message) {
               alert('WGB__OC注入JS改变网页内容' + message);
           }
           
           function wgb_testCallOCKeyboard() {
               jsCallOCAlertKeyboard();
           }
           
           function wgb_endEidting() {
               wgb_keyboardResignFirstResponder();
           }
           
           var getIOSImage = function () { ///方法一
               var parameter = {'title': 'JS调OC', 'describe': '这里就是JS传给OC的参数'};
               // 在下面这里实现js 调用系统原生api
               window.iosDelegate.getImage(JSON.stringify(parameter));// 实现数据的 json 格式字符串
           }
           
           function getImageFromOC() { ///方法二
               iOSImageUpLoad();
           }
           
           // 这里是 iOS调用js的方法
           function setJSImageWithPath(arguments) {
               /// 用代理的赋值操作
               //var element = document.getElementById('changePhoto');
               //element.src =arguments['urlStringPath'];
               //var iOSParameters =  document.getElementById('iOSParameters');
               // iOSParameters.innerHTML = arguments['iosContent'] + arguments['img'];
               
               ///方法二的赋值操作
               var element_wgb = document.getElementById('wgb_changePhoto');
               element_wgb.src = arguments['urlStringPath'];
               var parameter_wgb = document.getElementById('wgb_iOSParameters');
               parameter_wgb.innerHTML = arguments['iosContent'] + arguments['img'];
           }
           
               </script>
           
           <style type="text/css">
               .alert {
                   color: green;
                   font-size: 30px;
                   border: blue 1px solid;
               }
           
           .args {
               color: red;
               font-size: 25px;
               border: solid 1px red;
           }
           
           .keyboard {
               color: blueviolet;
               font-size: 30px;
               border: solid 1px orange;
               
           }
           
           
               </style>
           
           
   </head>
   
   <body>
       <button class="alert" type="button" onclick="showAlert()"> JS测试弹窗showAlert</button>
       <br><br>
       <button class="args" onclick="secondClick(1)"> 我是参数1</button>
       <br><br>
       <button class="args" onclick="secondClick(2)"> 我是参数2</button>
       <br><br>
       <button class="args" onclick="secondClick(3)"> 我是参数3</button>
       <br><br>
       <button class="keyboard" onclick="wgb_testCallOCKeyboard()"> 召唤原生键盘</button>
       <br><br>
       <button class="keyboard" onclick="wgb_keyboardResignFirstResponder()"> 退回原生键盘</button>
       <br>
       <br>
       
       <!--        <div>-->
       <!--            <input type = "button" style="width: 50%;height: 5%;" id="Button" value="打开相机获取图片" onclick="getIOSImage()"></button>-->
       <!--        </div>-->

<br>
<br>

<!--        <div>-->
       <!--            ![](testImage.png)<!--src="图片的相对路径" 如果把html文件导入工程中,图片路径和OC一样只写图片名字和后缀就可以,(记得要先把图片添加到工程) 图片也可以实现按钮的方法getIOSImage -->
       <!--        </div>-->
       <!--        <span id="iOSParameters" style="width: 200px; height: 50%; color:orangered; font-size:15px" value="等待获取ios参数" >-->
       <br>
       
       <button class="args" type="button"
           " onclick="getImageFromOC()"> 打开相册相机方式二 </button>
           <br>            <br>
           <br>
           <br>

           ![](test.jpg)
           <br>
           
           <span id="wgb_iOSParameters" style="width: 200px; height: 50%; color: red;font-size: 15px" value="等待获取ios参数">
           
           <div style="background: red;width: 200px;height: 200px">
           小方块
           </div>
           
           </body>
           </html>

将以上代码放到浏览器中渲染,只能看到几个按钮,一个破图片,点击的话也只有第一个弹框有反应,其他的都反应,因为内在逻辑实现的是按钮点击是有OC代码来反馈以及调用,比如弹起键盘,弹起原生的弹窗,收起键盘...打开系统相机相册等操作.

1. OC里调用JS的函数

一般这种情况是直接监听JS定义的函数

#pragma mark- 戳按钮,OC监听获取JS传过来的参数 [JS调用OC]
- (void)js_ocCallBack{
    JSContext *js = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    js[@"wgb_iOSHookJSButtonClick"] = ^(){
        NSArray *args = [JSContext currentArguments];
        NSString *arg = [args.firstObject toString];
        NSLog(@"arg = %@",arg);
        dispatch_async(dispatch_get_main_queue(), ^{
            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"JS调用OC,JS传过来的参数是" message: arg delegate:self cancelButtonTitle:@"确认" otherButtonTitles:nil];
            [alertView show];
        });
    };
}

//JS里的代码是这样的:
function secondClick(index) {
                    wgb_iOSHookJSButtonClick(index);
                }
///三个`button` `onclick`调用 `secondClick(1)` ,`secondClick(2)` `secondClick(3)`这样子,然后JS走index,  OC 调用`wgb_iOSHookJSButtonClick `,获取到参数值就是JS传过去的index,每次点击只传一个值.

2. JS里调用OC的函数

比如说JS调用原生的键盘啊,相机相册啥的...

#pragma mark- 弹起键盘
- (void)alertKeyboard{
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    context[@"jsCallOCAlertKeyboard"] = ^(){ 
///这个方法名和前端的同学约定好就ok的
    dispatch_async(dispatch_get_main_queue(), ^{
        UITextField *textfiled = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 100, 10)];
        [self.view addSubview: textfiled];
        textfiled.hidden = YES;
        [textfiled becomeFirstResponder];
      });
    };
}

#pragma mark- 退出原生键盘
- (void)wgb_keyboardResignFirstResponder{
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    context[@"wgb_keyboardResignFirstResponder"] = ^(){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.view endEditing:YES];
        });
    };
}

#pragma mark- 指定一个方法去调用 相机/相册 选择图片
- (void)js_callOCPhotoPickerImage{
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    context[@"iOSImageUpLoad"] = ^(){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.manager quickAlertSheetPickerImage];
        });
    };
}

以上这几个函数在网页加载完毕的时候就可以直接调用的
// 加载完成开始监听js的方法
- (void)webViewDidFinishLoad:(UIWebView *)webView{
   JSContext *jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    jsContext[@"iosDelegate"] = self;
    jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception){
        context.exception = exception;
        NSLog(@"获取 self.jsContext 异常信息:%@",exception);
    };
}


- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    [self js_ocCallBack];
    [self oc_jsCallBack];
    [self alertKeyboard];
    [self wgb_keyboardResignFirstResponder];
    [self js_callOCPhotoPickerImage]; 
    return YES;
}

Github Demo地址

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

推荐阅读更多精彩内容