對于 iOS Native 與 JS 交互我們先從調用方向上分為兩種情況來看:
JS 調用 Native
Native 調用 JS
- JS 調用 Native
其實 JS 調用 iOS Native 也分為兩種實現(xiàn)方式:
a. 假 Request 方法
b. JavaScriptCore 方法
a. 假 Request 方法
原理:其實這種方式就是利用了 webview 的代理方法,在 webview 開始請求的時候截獲請求,判斷請求是否為約定好的假請求。如果是假請求則表示是 JS 想要按照約定調用我們的 Native 方法,按照約定去執(zhí)行我們的 Native 代碼就好。
① UIWebView
UIWebView 代理有用于截獲請求的函數,在里面做判斷就好:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = request.URL;
// 與約定好的函數名作比較
if ([[url scheme] isEqualToString:@"your_func_name"]) {
// just do it
}
}
② WKWebView
WKWebView 有兩個代理,一個是 WKNavigationDelegate,另一個是 WKUIDelegate。WKUIDelegate 在另一篇文章中講到,這里我們需要設置并實現(xiàn)它的 WKNavigationDelegate 方法:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSURL *url = navigationAction.request.URL;
// 與約定好的函數名作比較
if ([[url scheme] isEqualToString:@"your_func_name"]) {
// just do it
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
decisionHandler 是當你的應用程序決定是允許還是取消導航時,要調用的代碼塊。 該代碼塊使用單個參數,它必須是枚舉類型 WKNavigationActionPolicy 的常量之一。如果不調用 decisionHandler 會引起 crash。
這里補充一下 JS 代碼:
function callNative() {
loadURL("your_func_name://xxx");
}
然后用一下就好了
b. JavaScriptCore 方法
iOS 7 有了 JavaScriptCore 專門用來做 Native 與 JS 的交互。我們可以在 webview 完成加載之后獲取 JSContext,然后利用 JSContext 將 JS 中的對象引用過來用 Native 代碼對其作出解釋或響應:
// 首先引入 JavaScriptCore 庫
#import <JavaScriptCore/JavaScriptCore.h>
// 然后再 UIWebView 的完成加載的代理方法中
- (void)webViewDidFinishLoad:(UIWebView *)webView {
// 獲取 JS 上下文
jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// 做引用,將 JS 內的元素引用過來解釋,比如方法可以解釋成 Block,對象也可以指向 OC 的 Native 對象哦
jsContext[@"iosDelegate"] = self;
jsContext[@"yourFuncName"] = ^(id parameter){
// 注意這里的線程默認是 web 處理的線程,如果涉及主線程操作需要手動轉到主線程
dispatch_async(dispatch_get_main_queue(), ^{
// your code
});
}
}
而 JS 這邊代碼更簡單了,干脆聲明一個不解釋的函數(約定好名字的),用于給 Native 做引用:
var parameter = xxx;
yourFuncName(parameter);
- iOS Native 調用 JS
iOS Native 調用 JS 的實現(xiàn)方法也被 JavaScriptCore 劃分開來:
a. webview 直接注入 JS 并執(zhí)行
b. JavaScriptCore 方法
a. webview 直接注入 JS 并執(zhí)行
在 iOS 平臺,webview 有注入并執(zhí)行 JS 的 API。
UIWebView
UIWebView 有直接注入 JS 的方法:
NSString *jsStr = [NSString stringWithFormat:@"showAlert('%@')", @"alert msg"];
[_webView stringByEvaluatingJavaScriptFromString:jsStr];
這個方法會返回運行 JS 的結果(nullable NSString *),它是一個同步方法,會阻塞當前線程!盡管此方法不被棄用,但最佳做法是使用 WKWebView 類的 evaluateJavaScript:completionHandler:method。
WKWebView
不同于 UIWebView,WKWebView 注入并執(zhí)行 JS 的方法不會阻塞當前線程。因為考慮到 webview 加載的 web content 內 JS 代碼不一定經過驗證,如果阻塞線程可能會掛起 App。
NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')", @"北京市東城區(qū)南鑼鼓巷納福胡同xx號"];
[_webview evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@----%@", result, error);
}];
這個方法不會阻塞線程,而且它的回調代碼塊總是在主線程中運行。
b. JavaScriptCore 方法
// 首先引入 JavaScriptCore 庫
#import <JavaScriptCore/JavaScriptCore.h>
// 先獲取 JS 上下文
self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// 如果涉及 UI 操作,切回主線程調用 JS 代碼中的 YourFuncName,通過數組@[parameter] 入參
dispatch_async(dispatch_get_main_queue(), ^{
JSValue *jsValue = self.jsContext[@"YourFuncName"];
[jsValue callWithArguments:@[parameter]];
});
上面的代碼調用了 JS 代碼中 YourFuncName 函數,并且給函數加了 @[parameter] 作為入參。這里再貼一下 JS 代碼:
function YourFuncName(arguments){
var result = arguments;
// do what u want to do
}