iOS之JS交互

ObjectiveC與Js交互是常見的需求,可對於新手或者所謂的高手而言,其實並不是那麼簡單明瞭。這裏只介紹iOS7.0後出來的JavaScriptCore framework。

關於JavaScriptCore

涉及到的幾種類型:

JSContext, JSContext是代表JS的執行環境,通過-evaluateScript:方法就可以執行一JS代碼
JSValue, JSValue封裝了JS與ObjC中的對應的類型,以及調用JS的API等
JSExport, JSExport是一個協議,遵守此協議,就可以定義我們自己的協議,在協議中聲明的API都會在JS中暴露出來,才能調用

ObjC與JS交互方式

通過JSContext,我們有兩種調用JS代碼的方法:

1、直接調用JS代碼
2、在ObjC中通過JSContext注入模型,然後調用模型的方法

直接調用JS代碼

// 一個JSContext對象,就類似於Js中的window,
// 只需要創建一次即可。
self.jsContext = [[JSContext alloc] init];

// jscontext可以直接執行JS代碼。

[self.jsContext evaluateScript:@"var num = 10"];
 [self.jsContext evaluateScript:@"var squareFunc = function(value) { return value * 2 }"];

// 計算正方形的面積

JSValue *square = [self.jsContext evaluateScript:@"squareFunc(num)"];

// 也可以通過下標的方式獲取到方法

JSValue *squareFunc = self.jsContext[@"squareFunc"];
 JSValue *value = [squareFunc callWithArguments:@[@"20"]];
 NSLog(@"%@", square.toNumber);
 NSLog(@"%@", value.toNumber);

這種方式是沒有注入模型到JS中的。這種方式使用起來不太合適,通常在JS中有很多全局的函數,爲了防止名字重名,使用模型的方式是最好不過了。通過我們協商好的模型名稱,在JS中直接通過模型來調用我們在ObjC中所定義的模型所公開的API。
通過注入模型的方式交互

首先,我們需要先定義一個協議,而且這個協議必須要遵守JSExport協議。

@protocol JavaScriptObjectiveCDelegate <JSExport>

// JS調用此方法來調用OC的系統相冊方法
- (void)callSystemCamera;
// 在JS中調用時,函數名應該爲showAlertMsg(arg1, arg2)
// 這裏是只兩個參數的。
- (void)showAlert:(NSString *)title msg:(NSString *)msg;
// 通過JSON傳過來
- (void)callWithDict:(NSDictionary *)params;
// JS調用Oc,然後在OC中通過調用JS方法來傳值給JS。
- (void)jsCallObjcAndObjcCallJsWithDict:(NSDictionary *)params;

@end

接下來,我們還需要定義一個模型:

// 此模型用於注入JS的模型,這樣就可以通過模型來調用方法。
@interface HYBJsObjCModel : NSObject <JavaScriptObjectiveCDelegate>

@property (nonatomic, weak) JSContext *jsContext;
@property (nonatomic, weak) UIWebView *webView;

@end

實現這個模型:

@implementation HYBJsObjCModel

- (void)callWithDict:(NSDictionary *)params {
 NSLog(@"Js調用了OC的方法,參數爲:%@", params);
}

// Js調用了callSystemCamera
- (void)callSystemCamera {
 NSLog(@"JS調用了OC的方法,調起系統相冊");

 // JS調用後OC後,又通過OC調用JS,但是這個是沒有傳參數的
 JSValue *jsFunc = self.jsContext[@"jsFunc"];
 [jsFunc callWithArguments:nil];
}

- (void)jsCallObjcAndObjcCallJsWithDict:(NSDictionary *)params {
 NSLog(@"jsCallObjcAndObjcCallJsWithDict was called, params is %@", params);


 // 調用JS的方法
 JSValue *jsParamFunc = self.jsContext[@"jsParamFunc"];
 [jsParamFunc callWithArguments:@[@{@"age": @10, @"name": @"lili", @"height": @158}]];
}

- (void)showAlert:(NSString *)title msg:(NSString *)msg {
 dispatch_async(dispatch_get_main_queue(), ^{
   UIAlertView *a = [[UIAlertView alloc] initWithTitle:title message:msg delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
   [a show];
 });
}

@end
接下來,我們在controller中在webview加載完成的代理中,給JS注入模型。
#pragma mark - UIWebViewDelegate
- (void)webViewDidFinishLoad:(UIWebView *)webView {
 self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
 // 通過模型調用方法,這種方式更好些。
  HYBJsObjCModel *model  = [[HYBJsObjCModel alloc] init];
 self.jsContext[@"OCModel"] = model;
 model.jsContext = self.jsContext;
 model.webView = self.webView;

 self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
   context.exception = exceptionValue;
   NSLog(@"異常信息:%@", exceptionValue);
 };
}
我們是通過webView的valueForKeyPath獲取的,其路徑爲documentView.webView.mainFrame.javaScriptContext。 這樣就可以獲取到JS的context,然後爲這個context注入我們的模型對象。 我們先寫兩個JS方法:
 var jsFunc = function() {
   alert('Objective-C call js to show alert');
 }

 var jsParamFunc = function(argument) {
   document.getElementById('jsParamFuncSpan').innerHTML
   = argument['name'];
 }

這裏我們定義了兩個JS方法,一個是jsFunc,不帶參數。
另一個是jsParamFunc,帶一個參數。

接下來,我們在html中的body中添加以下代碼:

<div style="margin-top: 100px">
<h1>Test how to use objective-c call js</h1>
<input type="button" value="Call ObjC system camera" onclick="OCModel.callSystemCamera()">
<input type="button" value="Call ObjC system alert" onclick="OCModel.showAlertMsg('js title', 'js message')">
</div>

<div>
<input type="button" value="Call ObjC func with JSON " onclick="OCModel.callWithDict({'name': 'testname', 'age': 10, 'height': 170})">
<input type="button" value="Call ObjC func with JSON and ObjC call js func to pass args." onclick="OCModel.jsCallObjcAndObjcCallJsWithDict({'name': 'testname', 'age': 10, 'height': 170})">
</div>

<div>
<span id="jsParamFuncSpan" style="color: red; font-size: 50px;"></span>
</div>

現在就可以測試代碼了。

當我們點擊第一個按鈕:Call ObjC system camera時,
通過OCModel.callSystemCamera(),就可以在HTML中通過JS調用OC的方法。
在OC代碼中,我們的callSystemCamera方法體中,添加了以下兩行代碼,就是獲取HTML中所定義的JS就去jsFunc,然後調用它。

 JSValue *jsFunc = self.jsContext[@"jsFunc"];
 [jsFunc callWithArguments:nil];

這樣就可以在JS調用OC方法時,也讓OC反饋給JS。

看看下面傳字典參數:

- (void)jsCallObjcAndObjcCallJsWithDict:(NSDictionary *)params {
 NSLog(@"jsCallObjcAndObjcCallJsWithDict was called, params is %@", params);

 // 調用JS的方法
 JSValue *jsParamFunc = self.jsContext[@"jsParamFunc"];
 [jsParamFunc callWithArguments:@[@{@"age": @10, @"name": @"lili", @"height": @158}]];
}

獲取我們在HTML中定義的jsParamFunc方法,然後調用它並傳了一個字典作爲參數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章