iOS--hybrid混合開發之 js與OC之間的交互


跟進hybrid混合項目開發已有一段時間了,一直都想著手總結一下js與OC的交互關系,但又感覺一直都還沒有摸透,總感覺還差點什么...

下面總結下最近學到的js與OC之間的交互:

簡單來說js與OC交互就是通過一些方法使得js可以調用OC中的方法,亦或者OC可以調用js中的方法,使得兩者可以互傳參數,進行各自處理;下面介紹各自兩種方法:

一、OC調用js方法,OC傳參數給js:

1.*- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray **)arguments;

這個方法可以讓我們可以直接在OC上簡單地調用JS上的方法。只是如果定義的方法是全局函數,那么很顯然應該在JSContext的glocalObject對象上調用該方法;如果是某JavaScript對象上的方法,就應該用相應的JSValue對象調用。

舉個例子,在js上有方法:
function buttonClicke()
{
      testobject.TestNOParameter();
}
那么方法“buttonClicke()”則是全局函數;
“TestNOParameter()”則是JavaScript對象上的方法;

    //比如:buttonClicke是全局函數,所以用globalObject調用,從OC傳遞arguments回到web:
    [[context globalObject] invokeMethod: buttonClicke withArguments:arguments];
    [[context globalObject] invokeMethod:@"CallBack" withArguments:@[@"你好世界",@"a",@"b",@"c"]];

    //比如:TestNOParameter是JavaScript對象上的方法,所以用相應的testobject調用,從OC傳遞arguments回到web:
   `?????但這個方法我不知道怎么寫,望知道的朋友告知一下////////`

    `百度上搜到的全是這段:
    JSValue還提供- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments;
    讓我們可以直接簡單地調用對象上的方法。只是如果定義的方法是全局函數,那么很顯然應該在JSContext的globalObject對象上調用該方法;
    如果是某JavaScript對象上的方法,就應該用相應的JSValue對象調用.`
    但是我比較笨,不知道所謂的"應該用相應的JSValue對象調用"應該怎么寫,請教大家;
2.*- (JSValue *)evaluateScript:(NSString **)script;

這個方法可以簡單地在OC上調用js的方法腳本;

比如:
 //通過oc方法調用js的alert
 [context evaluateScript:@"alert('test js OC')";];

再如js中有方法如下:
 function buttonClicke()
{
      testobject.alert(value);
}
OC中可以這樣調用:前面是先“對象.方法=”,后面再是“方法(回傳參數)”;
 [context evaluateScript:[NSString stringWithFormat:@"testobject.alert = alert('%@');",@"回傳"]];

二、js調用OC方法,js傳參數給OC:

1.使用block 直接調用
  如果js有方法:
  function buttonClicke()
 {
          Tank("參數");
 }
  則OC中可以直接使用block響應js的這個Tank方法:
  context[@"Tank"] = ^() {
        NSArray *arguments = [JSContext currentArguments];   //傳過來的是一個數組
        NSString *string = arguments[0];
  };
        //拿到參數后就可以干嘛干嘛了
        //或者不用傳參數,js那邊點擊了這個方法,OC這邊響應進入到block里,想干嘛就在這里干嘛就是了。

或者js有方法
function clicke()
{
        alert(value);
}

OC中可以直接執行并返回值給js用:
context[@"alert"] = ^() {
        return @"回傳參數";
};
2.使用JSExport 對象調用

在OC中凡事添加了JSExport協議的協議,所規定的方法、變量等就會對js開放,就可以通過js調用到;

通過JSExport協議的重載宏,可以告訴js調用什么方法時,會執行OC端的什么方法;

    如js方法為:testobject.TestOneParameter('參數1');
    //testobject 是 js 對象,TestOneParameter是方法名,后面為參數,方法名可隨意起,沒有規范,并不像有些博客上寫的要與協議方法拼起來的名字一樣,沒有必要的。
 
 *  首先創建一個類 繼承NSObject并遵循 規定好的這個協議,這個協議遵循JSExport協議:
            #import <Foundation/Foundation.h>
            #import <JavaScriptCore/JavaScriptCore.h>
 
            //首先創建一個實現了JSExport協議的協議
            @protocol TestJSObjectProtocol <JSExport>
 
            //使用JSExport重載宏JSExportAs來聲明方法:
            //點進JSExportAs里面看API,可以發現其規則:前面是js的方法如:TestOneParameter;
            后面是OC里的這個協議里的方法,(注意后面一定要有一個參數,即使js那邊的沒有參數,這邊也要這樣寫,大不了接收到的參數為null,這個規則在JSExportAs里寫有,note那里);
            這一句的意思是告訴js:當執行js端的TestOneParameter方法時(當然,這個方法可以隨意其他名字),會調用OC端的“ -(void)TestNOParameter:(id)args);”這個方法,也叫注冊
            JSExportAs(TestOneParameter, -(void)TestNOParameter:(id)args);
             
            @end
             
            //然后在創建的類遵循上邊的協議
            @interface EXWebViewBridge : NSObject<TestJSObjectProtocol>
             
            @end
 
 *  然后去.m里實現或者在其他遵循這個協議的類里實現
           -(void)TestNOParameter:(id)args
          {
                NSLog(@"this is ios TestNOParameter = %@",args);
          }
 
 *  將這個類創建一個新對象賦給JS的對象
    js是通過對象調用的,我們假設js里面有一個對象 testobject 在調用方法
          context[@"testobject"]=[EXWebViewBridge new];

//這樣就可以了,當JS那邊在調用一個方法時,
//如:testobject.TestOneParameter('參數1'),就會來到OC里的協議里使用OC的對象代替JS的對象 并調用對應的協議方法去實現,并傳參數過來。

三、下面是代碼部分

1,index.html文件
<!--//  Created by Tank on 16-10-18.-->  
<!--//  Copyright (c) 2016年 Tank. All rights reserved.-->  

<!DOCTYPE html>  

<html lang="en">  
  
<head>  
      
     <meta charset="utf-8">  
          
      <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">  
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />      
              
        <meta name="description" content="">  
                  
        <meta name="viewport" content="width=device-width; initial-scale=1.0">  
         <script type="text/javascript" src="index.js"></script>             
    
                      
  </head>  
  
  <!--  js調用OC 使用block  -->
  <button id="halle" onclick="buttonClicka()"> Tank</button>
  <button id="halle" onclick="buttonClickb()"> 直接reture</button>

  <!--  使用JSExport 對象調用方法  -->
  <button id="halle" onclick="buttonClicke()"> 無參</button>
  <button id="hallf" onclick="buttonClickf()"> 一參</button>
  <button id="hallg" onclick="buttonClickg()"> 兩參</button>
  <button id="hallg" onclick="buttonClickh()"> 多參</button>
  <button id="hallg" onclick="buttonClicki()"> doFoo</button>
  </body>  
  
</html>
2,index.js文件
///使用Block  js調用OC
function buttonClicka()
{
    Tank("參數");
}
function buttonClickb()
{
    value = getReture();
    alert(value);
}


///使用使用JSExport 對象調用方法  js調用OC
//js這邊沒有參數
function buttonClicke()
{
    testobject.TestNOParameter();
}
function buttonClickf()
{
    testobject.TestOneParameter("這是一個參數!")
}
function buttonClickg()
{
    testobject.TestOneParameterSecondParameter("這是第一個參數","這是第二個參數!");
}

//多個參數
function buttonClickh()
{
    testobject.testMoreParameters("一個","兩個","三個","四個");
}

//doFoo
function buttonClicki()
{
    testobject.doFoo("one","two");
}

function callBack()
{
    //alert(value);
    testobject.alert(value);
}
3,創建一個新類TestJSObject.h
//
//  TestJSObject.h
//  TestJSandOC
//
//  Created by Tank on 16/9/20.
//  Copyright ? 2016年 Tank. All rights reserved.
//

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

@protocol TestJSObjectProtocol <JSExport>

///Tank:使用這種必須得要帶一個參數以上,可以點JSExportAs進里面看;這里說的一個參數是OC這邊的方法,即使js那邊沒有參數傳過來,這里的方法也要帶一個參數,實現方法那邊也要有一個參數,這樣才不會報錯,如下面這個,js那邊沒有參數,oc這邊還是要有一個參數來接收的。
JSExportAs(TestNOParameter, -(void)TestNOParameter:(id)args);

///js那邊有一個參數,Oc這邊也要有一個參數接收
JSExportAs(TestOneParameter, -(void)TestOneParameter:(id)args);

///js那邊有兩個參數,OC這邊也要有兩個參數接收,當然也可以用一個數組去接收它所有的參數。注意方法名的寫法,可以規范成如下,也可以隨意,如上所說:
JSExportAs(TestOneParameterSecondParameter, -(void)TestTowParameter:(NSString *)message1 SecondParameter:(NSString *)message2);

///多個參數,用數組接收
JSExportAs(testMoreParameters, -(void)testMorePara:(id)args);

///再如下,告訴雙方,當js調用doFoo 時,OC就調用對應的方法“- (void)doFoo:(id)foo withBar:(id)bar);”;
///js傳過來的參數會一一對應到OC里的,如果不夠或無則顯示為null;
JSExportAs(doFoo, - (void)doFoo:(id)foo withBar:(id)bar);

@end

@interface TestJSObject : NSObject<TestJSObjectProtocol>
    ///這里不需要寫什么,只要讓這個類遵循上面所規定好的那個協議就好。
@end
4,這個新類的實現TestJSObject.m
//
//  TestJSObject.m
//  TestJSandOC
//
//  Created by Tank on 16/9/20.
//  Copyright ? 2016年 Tank. All rights reserved.
//

#import "TestJSObject.h"

@implementation TestJSObject

///js那邊沒有參數,這邊也要用一個來接收,接收回來的值為null
-(void)TestNOParameter:(id)message
{
    NSLog(@"this is ios TestNOParameter = %@",message);
}

-(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);
}

///ja傳來多個參數,OC使用數組接收
-(void)testMorePara:(id)args
{
    NSArray *arguments = [JSContext currentArguments];
    NSLog(@"arguments = %@",arguments);

    for (JSValue *content in arguments) {
        NSLog(@"參數分別是 = %@",content);
    }

    NSString *stringOne = [[arguments objectAtIndex:2] toString];
    NSLog(@"stringOne = 這是第%@參數",stringOne);
}

- (void)doFoo:(id)foo withBar:(id)bar
{
    NSLog(@"doFoo = %@,withBar = %@",foo,bar);
}

@end
5,ViewController.m
創建一個webView,并LoadRequest剛才創建的index.html;
在webView的代理方法:webViewDidFinishLoad 下執行:

self.myContext = [self.myWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

///使用對象JSExport
TestJSObject *testJS=[[TestJSObject alloc] init];
///把OC對象 賦給 js對象
self.myContext[@"testobject"]=testJS;


//使用Block
self.myContext[@"Tank"] = ^(){
  
    NSArray *arguments = [JSContext currentArguments];
    for (NSString *args in arguments) {
        NSLog(@"args = %@",args);
    }
    //或者
    NSString *string = [[arguments objectAtIndex:0] toString];
};

self.myContext[@"getReture"] = ^(){
  return @"直接返回一個值給js";
};


///OC調用JS的兩個方法:
///直接調用,或者將方法名與參數分開調用
[context evaluateScript:@"alert('test js OC')"]; //如果有變量可以使用stringWithFormat
[[context globalObject] invokeMethod:@"alert" withArguments:@[@"test js OC"]];
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容

  • 注:本文copy自http://www.lxweimin.com/p/ac534f508fb0,純屬當筆記使用。 概...
    BookKeeping閱讀 738評論 1 3
  • JavaScriptCore框架主要是用來實現iOS與H5的交互。由于現在混合編程越來越多,H5的相對講多,所以研...
    水靈芳蕥閱讀 1,423評論 1 8
  • 隨著H5技術的興起,在iOS開發過程中,難免會遇到原生應用需要和H5頁面交互的問題。其中會涉及方法調用及參數傳值等...
    Chris_js閱讀 3,099評論 1 8
  • 一、JavaScriptCore常用的類 JavaScriptCore作用:JavaScriptCore是蘋果原生...
    CoderZS閱讀 908評論 0 8
  • 本文由我們團隊的 糾結倫 童鞋撰寫。 寫在前面 本篇文章是對我一次組內分享的整理,大部分圖片都是直接從keynot...
    知識小集閱讀 15,274評論 11 172