通攔截協議頭來獲取協議字符串。在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()是異步執行的。