1. 程式人生 > >iOS與JS互動

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

:JSExport是一個協議,一個協議A,只有當A遵守JSExport協議時,A中的方法才能被JS呼叫。

Objective-C與JS互動

通過JSContext,有兩種方式與JS互動:

  1. 通過-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
  1. 向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頁面