对于 iOS Native 与 JS 交互我们先从调用方向上分为两种情况来看:
JS 调用 Native
Native 调用 JS
- JS 调用 Native
其实 JS 调用 iOS Native 也分为两种实现方式:
a. 假 Request 方法
b. JavaScriptCore 方法
a. 假 Request 方法
原理:其实这种方式就是利用了 webview 的代理方法,在 webview 开始请求的时候截获请求,判断请求是否为约定好的假请求。如果是假请求则表示是 JS 想要按照约定调用我们的 Native 方法,按照约定去执行我们的 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 在另一篇文章中讲到,这里我们需要设置并实现它的 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 的实现方法也被 JavaScriptCore 划分开来:
a. webview 直接注入 JS 并执行
b. JavaScriptCore 方法
a. webview 直接注入 JS 并执行
在 iOS 平台,webview 有注入并执行 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 注入并执行 JS 的方法不会阻塞当前线程。因为考虑到 webview 加载的 web content 内 JS 代码不一定经过验证,如果阻塞线程可能会挂起 App。
NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')", @"北京市东城区南锣鼓巷纳福胡同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
}