最近做的项目中有一个需要在web页中判断APP是否登陆,如果没有登陆跳转到APP的登陆界面去登陆。而在这里面技术方面主要就是涉及到web端
和服务端的交互,web前端
和iOS
、Android
的交互。
iOS原生应用和web页面的交互大致上有这几种方法iOS7之后的JavaScriptCore
、拦截协议
、第三方框架WebViewJavaScriptBridge
、iOS8之后的WKWebView。
我在项目中用的是JavaScriptCore方法,首先导入JavaScriptCore.framework库,然后创建一个webView,初始化,添加,打开网址。下面实现UIWebView的代理方法。
- (void)webViewDidFinishLoad:(UIWebView *)webView{
//在OC中获取JS的上下文
JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//其中toAppLogin就是js的方法名称,赋给是一个block 里面是iOS代码
//打印出所有接收到的参数
context[@"toAppLogin"] = ^() {
NSLog(@"js调用oc---------begin--------");
NSArray *args = [JSContext currentArguments];
for (JSValue *jsValue in args) {
NSLog(@"%@",jsValue);
}
NSLog(@"js调用oc---------The End-------");
};
}
这段代码 在 webView 加载完成,用户交互时没有问题,但是如果页面刚加载的时候,H5 就需要和客户端通过js 交互,让客户端回传参数才能显示页面时候,显然是有问题的。然后我又把
jContext
回调赋值放在
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
//在OC中获取JS的上下文
JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//其中toAppLogin就是js的方法名称,赋给是一个block 里面是iOS代码
//打印出所有接收到的参数
context[@"toAppLogin"] = ^() {
NSLog(@"js调用oc---------begin--------");
NSArray *args = [JSContext currentArguments];
for (JSValue *jsValue in args) {
NSLog(@"%@",jsValue);
}
NSLog(@"js调用oc---------The End-------");
};
return YES;
}
这么写还是有问题。获取不到js的方法名称。
后来查了一下原因,感觉是我注入交互对象的时机跟我用
如果我们在- (void)webViewDidFinishLoad:(UIWebView )webView方法中注入JS,看起来貌似可以解决重定向之后调用失效的问题,因为webView每次加载完成后都会回调- (void)webViewDidFinishLoad:(UIWebView )webView,也就是说每次重定向之后,只要页面加载完成,JS代码就会重新被注入。如果JS调用OC方法的时机是在页面加载完成之后,比如点击web界面上的按钮或者由用户手动触发一个事件调用OC代码,这种情况一定是web页面加载完成之后才会发生的,而此时我们已经重新注入了JS,这样一点问题都没有。但是,如果JS调用OC方法的时机刚好发生在页面加载过程中呢?比如web界面加载过程中自动执行一些操作需要调用OC代码,而此时- (void)webViewDidFinishLoad:(UIWebView *)webView还没有回调,所以我们的JS代码并没有重新注入,这里仍然会造成失效的问题。应该在每次创建
JavaScript
调用交互方法的时机不对。当我们在- (void)viewDidLoad中注入JS代码之后,如果页面发生了重定向,此时web页面的JS已经发生了变化,而- (void)viewDidLoad方法只会执行一次,所以不再是之前我们注入过的那些JS了,此时再调用本地方法自然就失效了。如果我们在- (void)webViewDidFinishLoad:(UIWebView )webView方法中注入JS,看起来貌似可以解决重定向之后调用失效的问题,因为webView每次加载完成后都会回调- (void)webViewDidFinishLoad:(UIWebView )webView,也就是说每次重定向之后,只要页面加载完成,JS代码就会重新被注入。如果JS调用OC方法的时机是在页面加载完成之后,比如点击web界面上的按钮或者由用户手动触发一个事件调用OC代码,这种情况一定是web页面加载完成之后才会发生的,而此时我们已经重新注入了JS,这样一点问题都没有。但是,如果JS调用OC方法的时机刚好发生在页面加载过程中呢?比如web界面加载过程中自动执行一些操作需要调用OC代码,而此时- (void)webViewDidFinishLoad:(UIWebView *)webView还没有回调,所以我们的JS代码并没有重新注入,这里仍然会造成失效的问题。应该在每次创建
JSContext环境
的时候,我们都去注入此交互对象这样就解决了上面的问题。于是我找到了相关的第三方webview的延展UIWebView+TS_JavaScriptContext。解决了该问题。(有可能AppStore审核会被拒绝,说是因为使用了私有的API。我还没有遇到该情况。)
- (void)webView:(UIWebView *)webView didCreateJavaScriptContext:(JSContext *)ctx
{
//其中toAppLogin就是js的方法名称,赋给是一个block 里面是iOS代码
ctx[@"toAppLogin"] = ^(){
NSArray *args = [JSContext currentArguments];
for (JSValue *jsValue in args) {
//打印出所有接收到的参数
NSLog(@"--%@",jsValue);
}
};
}
具体解决办法参考了此开源库UIWebView-TS_JavaScriptContext。