通拦截协议头来获取协议字符串。在UIWebView中的代理方法中有这样的方法,以下图所示:javascript
//UIWebView每次请求内容以前,都会调用这个方法,经过返回YES/NO来决定UIWebView是否进行request请求。 //咱们能够经过URL的协议头及字符串来区别普通的URL请求 //JS传递给OC的参数能够经过URL带过来,若是参数内容过长能够经过post请求来传递,本地在拦截request后,能够将HTTPBody中的请求内容解析出来。 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ return YES; }
下面是我写的简单的H5页面经过JS请求一个自定义协议的URL,而后经过UIWebView来拦截:html
H5和JS代码以下:java
<!-- H5代码 --> <input type="button" onclick="shareWexin()" value="无参协议的URL"> <!-- JS 代码 --> <script type="text/javascript"> function shareWexin() { //这里objc是协议头,做为iOS端拦截的标识 //shareWX即为‘协议名’、做为iOS方法的识别 window.location.href="objc://shareWX"; } </script>
UIWebView代理中的协议拦截:web
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ NSString *urlString = [[request URL] absoluteString]; NSLog(@"协议URL => %@",urlString); NSArray *urlComps = [urlString componentsSeparatedByString:@"://"]; if([urlComps count] && [[urlComps objectAtIndex:0] isEqualToString:@"objc"]){ NSLog(@"方法名 => %@",urlComps.lastObject); return NO; } return YES; }
点击网页中的按钮打印以下所示:异步
如今咱们成功拦截到了协议,后续的处理就不用我多说了吧!post
H5和JS代码以下:测试
<body> <h2>第一种方法</h2> <h4>请求有参协议的URL</h4> <!-- H5代码 --> <input type="button" onclick="shareQQ()" value="有参协议的URL"> <!-- JS 代码 --> <script type="text/javascript"> function shareQQ() { //这里objc是协议头,做为iOS端拦截的标识 //shareWX即为‘协议名’、做为iOS方法的识别 //type=qq 即为参数 window.location.href="objc://shareQQ?type=qq"; } </script> </body>
UIWebView代理中的协议拦截:lua
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ NSString *urlString = [[request URL] absoluteString]; NSLog(@"协议URL => %@",urlString); NSArray *urlComps = [urlString componentsSeparatedByString:@"://"]; if([urlComps count] && [[urlComps objectAtIndex:0] isEqualToString:@"objc"]){ NSLog(@"方法名及参数URL => %@",urlComps.lastObject); NSString * parmeterURL = [urlComps lastObject]; //获取参数 NSArray * parmeters = [parmeterURL componentsSeparatedByString:@"?"]; NSString * parStr = [parmeters lastObject]; NSLog(@"携带参数的字符串 => %@",parStr); return NO; } return YES; }
点击网页中的按钮打印以下所示:url
如今咱们成功拦截到了协议中的方法名和参数,这样你就能够进行后续的处理了!spa
问题:
这种方法主要是经过JS调用一个咱们本身的通用协议URL,iOS端经过拦截的方法,来达到JS调用OC的目的,可是这存在很多问题,好比JS若是连续请求两次以上协议,iOS端只能拦截最后一个,这样就没法真正的作到JS调用OC;另外经过拦截的方法的在执行效率上比较慢!
那么还有更好的方法么?固然这就须要咱们用到JavaScriptCore来实现了,一下是第二种方法:
经过注入JS代码来消除JS异常状况,并实现上下文的Block回调,来实现调用OC的目的,下面为具体的步骤:
1、引入JavaScriptCore库:
@import JavaScriptCore;
2、在页面加载完成后,获取JS上下文:
//获取JS上下文 JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
3、注入JS代码来触发block:
//定义好JS要调用的方法,如shareWB context[@"shareWB"] = ^() { // your code };
第一种: 无参数的方法
经过JS调用和iOS端协商好的方法,在JS中并未实现,因此在JS中属于异常的状况,下面为H5和JS代码:
<h2>第二种方法</h2> <input type="button" onclick="shareWebo()" value="第二种方法"> <script type="text/javascript"> function shareWebo() { //这是和iOS端协商好的方法,在JS中并未实现,因此在JS中属于异常的状况 shareWB(); } </script>
捕获异常,即注入JS代码使消除异常状况,OC主要代码:
- (void)webViewDidFinishLoad:(UIWebView *)webView{ NSLog(@"页面加载完成"); //获取JS上下文 JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; //定义好JS要调用的方法,如shareWB context[@"shareWB"] = ^() { NSLog(@"shareWB 我被调用了!"); NSArray *args = [JSContext currentArguments]; for (JSValue *jsVal in args) { NSLog(@"%@", jsVal.toString); } }; }
点击网页中的按钮打印以下所示:
第二种: 有参数的方法
下面为H5和JS代码:
<h4>有参数的方法</h4> <input type="button" onclick="shareMessage()" value="有参数的方法"> <script type="text/javascript"> function shareMessage(){ //带参数的 未实现的方法, JS中一样属于异常的状况 shareMsg("p1","p2"); } </script>
OC主要代码:
- (void)webViewDidFinishLoad:(UIWebView *)webView{ NSLog(@"页面加载完成"); //获取JS上下文 JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; context[@"shareMsg"] = ^() { NSLog(@"shareMsg 我被调用了!"); NSArray *args = [JSContext currentArguments]; for (JSValue *jsVal in args) { NSLog(@"参数 => %@", jsVal.toString); } }; }
点击网页中的按钮打印以下所示:
问题:
能够同时触发多个事件么?如下是测试结果:
在JS中调用两个方法:
<script type="text/javascript"> function shareMessage(){ //带参数的 未实现的方法, JS中一样属于异常的状况 shareWB(); shareMsg("p1","p2"); } </script>
OC中的打印结果:
经测试,答案是确定的!
下面咱们探讨一下OC Call JS:
经过向UIWebView注入JS方法来调用JS内部的方法,以下所示:
- (IBAction)callJS:(id)sender { //调用js方法的字符串 NSString *jsStr = [NSString stringWithFormat:@"alertMsg('%@')",@"提示的信息"]; //将js注入webView [self.webView stringByEvaluatingJavaScriptFromString:jsStr]; }
如下是JS主要代码:
<script type="text/javascript"> //被oc调用的方法 function alertMsg(msg) { alert(msg); } </script>
如下是运行的效果,看这个弹出能够JS调用的哦:
使用JavaScriptCore来和JS交互
- (IBAction)callJS:(id)sender { //获取JS上下文 JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; //JS代码字符串 NSString *JS = @"alertMsg('提示信息2')"; [context evaluateScript:JS]; }
如下是JS主要代码:
<script type="text/javascript"> //被oc调用的方法 function alertMsg(msg) { alert(msg); } </script>
如下是运行的效果,看这个弹出能够JS调用的哦:
注意:
以上两种方法都是同步方法,若是JS比较耗时,会形成界面卡顿,建议将JS耗时的程序放在异步线程中执行!好比alert()方法就会阻塞UI主线程(注意:按钮‘注入JS’此时为高亮状态,说明弹窗阻塞了主线程,在等待用户响应),咱们能够经过setTimeout()方法来实现异步执行的目的,以下所示:
<script type="text/javascript"> //被oc调用的方法 function alertMsg(msg) { //异步执行 setTimeout(function(){ alert(msg); },1); } </script>
如下为运行结果,‘注入JS’按钮恢复了正常状态,说明此时alert()是异步执行的。