隨着H5的強大,hybrid app已經成為當前互聯網的大方向,單純的native app和web app在某些方面顯得就很劣勢。關於H5的發展史,這里有一篇文章推薦給大家,今天我們來學習最基礎的基於iOS系統的OC與JS之間是如何進行交互的,本文介紹的是基於UIWebView"協議攔截"實現的交互方式,當然后面還會循序漸進的介紹其他的交互方式。這里的說到的JS指的是廣義上JS,並不是單純的javascript,你可以理解為web前端的三件套(html+css+javascript);這里說的OC指的是iOS的系統語言Objective-C,為什么叫做OC與JS交互而不是iOS與JS交互或者其他名字,這個不是重點,也有叫web交互,H5交互的。本着盡可能清楚解釋原理的目標,文章的組織形式采用圖文並茂加示例代碼
先來一張圖,如下。簡要說明下:界面分為兩部分,上半部分是UIWebView加載的本地html頁面,下半部分是原生UI繪制的界面。我們這里需要實現的功能是,分別點擊上面的(小黃)三個按鈕,會執行OC里面對應的無參,1個參數,2個參數的方法;點擊下面的(小紅)三個按鈕,會執行HTML里面對應的無參,1個參數,2個參數的JS方法。下面的介紹會結合這張圖,及相關代碼來詮釋如何用原生UIWebView攔截協議的方式實現JS交互的。本文的示例代碼會放在文章的后面,需要的同學拿去不謝,可以先下載示例DEMO查看效果

OC與JS交互是雙向的,一方面是OC向JS發送消息,另一方面是JS向OC發送消息。代碼上的表現形式就是方法的相互調用,分為兩種:
一、OC調用JS方法
UIWebView內置一個方式可以執行JavaScript代碼,因此OC調用JS比較方便點
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
調用這個方法需要在網頁加載完成之后,因為這個時候整個html頁面包括js/css已經注入到webView中,此時調用方法才會有響應,相反網頁加載完成之前調用界面不會有任何響應
參考上圖,我們點擊小紅部分的事件按鈕,會調用JS中的事件處理代碼
OC部分:
if (sender.tag == 123) {
[self.webView stringByEvaluatingJavaScriptFromString:@"alertMobile()"];
}
if (sender.tag == 234) {
[self.webView stringByEvaluatingJavaScriptFromString:@"alertName('小紅')"];
}
if (sender.tag == 345) {
[self.webView stringByEvaluatingJavaScriptFromString:@"alertSendMsg('18870707070','周末爬山真是件愉快的事情')"];
}
JS部分:
function alertMobile() {
alert('我是上面的小黃 手機號是:13300001111')
}
function alertName(msg) {
alert('你好 ' + msg + ', 我也很高興見到你')
}
function alertSendMsg(num,msg) {
alert('這是我的手機號:' + num + ',' + msg + '!!')
}
二、JS調用OC方法
UIWebView加載過程中會有一系列代理方法,這里不關注其他的方法,只關注UIWebView在加載之前的一個代理方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
這是UIWebView在加載之前或者網頁進行重定向的時候調用的一個方法,而我們JS調用OC采用協議攔截方式實現的細節就是在這個方法里面完成的
有了上面的方法后,很顯然,想要JS調用OC我們就可以采用在按鈕點擊的后重定向一個URL,這個URL攜帶OC的方法名及參數信息,然后在這個方法中拿到對應的URL,對URL進行解析,提取對應的方法名和參數信息,調用OC相應的方法,從而實現了交互的可能,下面是示例中的URL
rrcc://showSendNumber_msg_?13300001111&go climbing this weekend
在這個URL中前面的"rrcc://"是URL的scheme,通過這個來提取我們關心的URL,對其他URL不做任何處理,后面就是OC方法和參數的信息了,這里用"?"來分割分割方法名和參數,"&"來分割多個參數,"_"用作OC方法名中冒號的替換。如果你願意,可以使用任何幾個字符來定義這個規則,這里采用的URL中經常會見到的字符。下面貼出部分示例代碼
JS部分:
function btnClick1() {
location.href = "rrcc://showMobile"
}
function btnClick2() {
location.href = "rrcc://showName_?xiaohuang"
}
function btnClick3() {
location.href = "rrcc://showSendNumber_msg_?13300001111&go climbing this weekend"
}
OC部分:
NSArray *components = [subPath componentsSeparatedByString:@"?"];
NSString *methodName = [components firstObject];
methodName = [methodName stringByReplacingOccurrencesOfString:@"_" withString:@":"];
SEL sel = NSSelectorFromString(methodName);
NSString *parameter = [components lastObject];
NSArray *params = [parameter componentsSeparatedByString:@"&"];
if (params.count == 2) {
if ([self respondsToSelector:sel]) {
[self performSelector:sel withObject:[params firstObject] withObject:[params lastObject]];
}
}
三、含參數的方法調用
OC調用JS方法,如果有參數,直接在方面名后面的括號中寫入對應的參數即可;如果是含有多個參數,參數之間用","分開
JS調用OC方法,參數是攜帶在URL中,通過解析URL來調用對應的方法
四、原生UIWebView交互的優缺點
下面我們來分析下這種交互方式:
1. 由於OC執行選擇器(selector)方法的限制,這種方式最多只能傳遞參數的個數為2個,如果需要多個參數,(開個腦洞)可以從數據結構的組織方面入手
2. 每次需要發生交互的時候我們都需要來自定義一個URL,然后解析URL,最后提取參數,合成方法名,找到對應的方法來調用;如果項目中有幾十上百個地方需要交互來完成,這樣一一來寫的話,顯然不是聰明的辦法,到時候你的代碼也會是雜亂,難以維護的,所以這種交互方式適用於項目中有少量,極個別的地方需要交互的需求。
3. 我們嫌麻煩,前輩們肯定也會嫌麻煩,有沒有解決辦法呢?iOS7之前,蘋果沒有出 JavaScriptCore 之前,業界普遍采用開源庫WebViewJavascriptBridge和EasyJSWebView來解決的,原理都是基於攔截協議的封裝,采用率第一個要遠遠高於第二個,我們將在下一篇文章中介紹WebViewJavascriptBridge的具體使用方法
戳這里:本文的DEMO地址歡迎star
