iOS與JS互動
1.使用JavaScriptCore
JavaScriptCore中常用的型別:
- JSContext :JSContext代表JS的執行環境,它的物件通過
-evaluateScipt:
方法就可以執行JS程式碼。可以通過
JSContext *jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
來從webview上獲取相應的JSContext。
- JSValue :JSValue中封裝了JS與ObjC中對應的型別,實現相互間的轉換,以及呼叫JS的API等。
- JSExport
Objective-C與JS互動
通過JSContext,有兩種方式與JS互動:
- 通過
-evaluateScipt:
方法直接呼叫JS程式碼
JSContext *context = [[JSContext alloc]init];
[context evaluateScript:@"function add(a){return a + 10;}"]; // 定義函式
[context evaluateScript:@"var num = 5;"]; // 定義變數
JSValue *addFunc = context[@"add"]; // 獲取add函式
JSValue *numVar = context[@"num"]; // 獲取變數num
JSValue *result = [addFunc callWithArguments:@[numVar]] ;
NSLog(@"%@",result); // 輸出 15
- 向JSContext中注入物件模型,然後呼叫模型的方法
HTML,在body標籤中寫如下程式碼:
<script>
function callback(something){
var target = document.getElementById('result');
target.innerHTML = something;
}
function alertCallback(aString,bString) {
alert(aString+bString);
}
</script>
<br/>
<br/>
<div>
<input type="button" value="呼叫一個引數或無引數OC方法" onclick="OC.showMessage('are you Objective-C?')">
<input type="button" value="呼叫多引數的OC方法" onclick="OC.mixAAndB('hellow','world')">
</div>
<br/>
<br/>
<div>
<input type="button" value="呼叫OC方法並回調" onclick="OC.doSomethingThenCallBack('make it happen')">
</div>
<br/>
<br/>
<div>
<h4>回撥結果:</h4>
<span id="result"></span>
</div>
首先,在script標籤中聲明瞭兩個回撥方法,供iOS端來呼叫;
之後聲明瞭三個button來呼叫不同的iOS的方法,第一個是一個引數或無引數的方法showMessage
,第二個是多引數的方法- mixAAndB
。
之所以要區分單個引數和多引數的情況,是因為當多個引數時,如iOS端的方法宣告是- (void)mixA:(NSString *)a andB:(NSString *)b;
在JS呼叫時應該將方法名連起來,調整大小寫,mixAAndB(a,b)
。
三個方法都是通過一個名為 OC
的物件呼叫,這個物件是要在iOS端注入的物件。
最後,callback方法中,將回調結果展示在一個span標籤當中。
在協議中宣告方法時,可以使用JSExport
中的JSExportAs
巨集來縮短JS端呼叫時使用的方法名,這樣JS端呼叫時只需要testArgumentTypes(i,d,b,s,n,a,o)
即可。
@protocol SomeProtocol <JSExport>
JSExportAs(testArgumentTypes,
- (NSString *)testArgumentTypesWithInt:(int)i double:(double)d
boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n
array:(NSArray *)a dictionary:(NSDictionary *)o
);
@end
在iOS的viewController的程式碼(僅做測試,不考慮程式碼是否合理了=。=):
首先,宣告一個協議JSInteract
並讓它遵守JSExport
協議,在協議中宣告需要讓JS呼叫的方法:
#import <JavaScriptCore/JavaScriptCore.h>
@protocol JSIneract <JSExport>
- (void)showMessage:(NSString *)message;
- (void)doSomethingThenCallBack:(NSString *)message;
- (void)mixA:(NSString *)aString andB:(NSString*)bString;
@end
接下來,在webview完成載入時,獲取JSContext,並注入 OC
物件(即為vc本身),在vc中實現協議中宣告的方法:
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.webview.delegate = self;
NSURL *htmlUrl = [[NSBundle mainBundle]URLForResource:@"interact" withExtension:@"html"];
[self.webview loadRequest:[NSURLRequest requestWithURL:htmlUrl]];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
self.context[@"OC"] = self; // 注入JS需要的“OC”物件
}
- (void)showMessage:(NSString *)message
{
NSLog(@"current:%@",[NSThread currentThread]);// 子執行緒
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"JS 呼叫了 OC" message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleCancel handler:nil];
[alert addAction:cancel];
[self presentViewController:alert animated:YES completion:nil];
});
}
- (void)doSomethingThenCallBack:(NSString *)message
{
NSString *result = [message stringByAppendingString:@"<br/>OC get message from JS then call back."];
JSValue *callback = self.context[@"callback"];
[callback callWithArguments:@[result]];
}
- (void)mixA:(NSString *)aString andB:(NSString *)bString
{
NSLog(@"A:%@ and B:%@",aString,bString);
JSValue *alertCallback = self.context[@"alertCallback"];
[alertCallback callWithArguments:@[aString,bString]];
}
需要注意的是,JS呼叫iOS方法時,該方法會在子執行緒執行,如果需要重新整理UI要切換到主執行緒,而回調JS方法時,保持在子執行緒即可。
效果:
使用Safari對webview進行除錯
使用Safari對webview進行除錯
模擬器或真機,在設定–Safari–高階 中開啟Web檢查器(web Inspector)
Mac Safari 瀏覽器,偏好設定–高階–開啟’開發’選單
在開發選單中即可連線當前機器正在開啟的webview頁面