實例:iOS 中的 JS 交互 OC & Swift 雙語
寫在前面
隨著 App 開發日趨成熟,不少設計模式和開發流程都被應用其中,也跟著日趨成熟。其中一條相信很多人都聽說過,不少項目也是這樣做的:
用 H5 頁面適配代替 Native App 原生開發語音開發的頁面
其實想一想不難理解為什么要這樣做?優勢有很多,應用場景也有很多。比方說一個非核心功能模塊,需求多變需要實時更新功能,此時如果用 Native App 原生開發語言開發就顯得有些不太適合,尤其是 iOS 方面自己寫出的代碼邏輯一時之間很難滿足比較大的需求變動或者考慮到以后所有的變化可能性就需要維護代碼,還要 Apple 審核方可更新 App Store 中的 App 版本,這時候用 Web 頁面內嵌 App 可用的系統瀏覽器顯示相關模塊的頁面給用戶就顯得靠譜許多。而且很多移動互聯網公司都是 Android 與 iOS 和 Web 風格幾乎統一的三管齊下模式,用 Web 內嵌入 App 顯示還省時省力,不僅輕松統一了 Android 和 iOS 客戶端相關頁面還幫兩方的小伙伴節省出時間精力去做好客戶端的核心功能。
Web 頁面與 Native App 交互
我們上面水了那么多話就是為了說明為啥要使用 Web 頁面內嵌 App。那么接下來就要說一下如果采用了 Web 頁面內嵌 App 的話需要解決的問題:
-
需要適配手機,pad,頁面高仿 App 原生頁面。(這個我們不管,工作板上找到 H5 的同事們,把需求丟給他們就好了)
-
有些 Web 頁面需要與 App 內部做交互,達到不僅看著像 App 原生界面,用起來還能跳轉到我們其他 App 原生界面做交互的境界
暫時就想到這么多,不過滿足了上面兩條,相信這個 Web 頁面內嵌對于用戶來說就很成功了。
情景模擬
模擬還是拿自己說事兒吧,這些天負責做產品的社區模塊(之前負責社區模塊的女同事產假 ing,所以這塊是大家分攤著做的),有個需求是社區中的帖子(圖文混排)需要加入點擊可以瀏覽大圖以及保存的功能。可是 H5 那邊的頁面圖片是沒有點擊事件的,這難不倒偉大的編碼勞動人民。查了一下資料早有前輩遇到此坑,簡單的介紹一下解決方法:就是利用 JS 嵌入 H5 頁面,在 JS 中為 H5 中的圖片綁定一個點擊事件,并給點擊事件寫特定的標識;之后在點擊事件觸發時獲取當前點擊的圖片 Url 進行瀏覽處理(瀏覽這塊我在這里不贅述了,主要是把相關的 JS 貼一下,簡單解讀一下)。
相關代碼
先貼一下 OC 的代碼,由于我們項目要兼顧 iOS 8.0 之前的 iOS 系統,所以我就先貼出 UIWebView 對應的代碼,(關于 WKWebView 的之后放出):
首先是加載請求
obj-C
#pragma mark - Foundations
(void)doLoadRequest {
NSURL url = [NSURL URLWithString:@"urlRequest = [NSURLRequest requestWithURL:url];
[(UIWebView *)self.webView loadRequest:urlRequest];
}
(void)showBigImgWithUrl:(NSString *)url {
NSLog(@"查看大圖,圖片 url <%@>", url);
}</code></pre>
Swift
// MARK: - Foundations
func doLoadRequest() {
let url = NSURL.init(string: "http://blog-lision.com/2016/05/26/iOS-Multithreading/");
let urlRequest = NSURLRequest.init(URL: url!);
webV.loadRequest(urlRequest);
}
func showBigImgWithUrl(url urlString : String) {
print("查看大圖,圖片 url <%s>", urlString);
}</code></pre>
再來截取請求
這里在開始加載請求時截獲 url,并根據 url 中 特定標識 用 cocoa 原生 API 做相關動作,以達到 JS 與 App 內部交互的目的
obj-C
#pragma mark - UIWebViewDelegate
(BOOL)webView:(UIWebView )webView shouldStartLoadWithRequest:(NSURLRequest )request navigationType:(UIWebViewNavigationType)navigationType {
//預覽圖片, image-preview 與之前 JS 里面添加的要對應
if ([request.URL.scheme isEqualToString:@"image-preview"]) {
NSString *path = [request.URL.absoluteString substringFromIndex:[@"image-preview:" length]];
path = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
//path 就是被點擊圖片的url
if ([self.mImgUrls containsObject:path]) {
[self showBigImgWithUrl:path];
}
return NO;
}
return YES;
}</code></pre>
Swift
// MARK: - UIWebViewDelegate
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
//預覽圖片, image-preview 與之前 JS 里面添加的要對應
if (request.URL?.scheme == "image-preview") {
let length = request.URL?.scheme.characters.count;
var path = ((request.URL?.absoluteString)! as NSString).substringFromIndex(length!);
path = path.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!;
//path 就是被點擊圖片的url
if (mImgUrls.contains(path)) {
showBigImgWithUrl(url: path);
}
return false;
}
return true;
}</code></pre>
完全加載后
完全加載之后按照我上述的情景應該要給當前頁面的所有本來不具備點擊事件(我的博客內部圖片有點擊事件,只是我項目中的實際需求沒有,但是不便于給大家貼實際的鏈接,不過效果一樣)添加點擊事件,并設下標識,等點擊之后從上面開始加載請求處截取對應標識的請求,用需求相關的事件調用即可。( 這里主要用 JS 嵌入頁面來獲取當前頁面的所有圖片并給圖片加入點擊事件 )
obj-C
- (void)webViewDidFinishLoad:(UIWebView *)webView {
//這里是js,主要目的實現對url的獲取
static NSString * const jsGetImages =
@"function getImages(){\
var objs = document.getElementsByTagName(\"img\");\
var imgScr = '';\
for (var i = 0; i < objs.length; ++i){\
imgScr = imgScr + objs[i].src + '+';\
};\
return imgScr;\
};";
[webView stringByEvaluatingJavaScriptFromString:jsGetImages]; // 注入js方法
NSString *urlResurlt = [webView stringByEvaluatingJavaScriptFromString:@"getImages()"];
self.mImgUrls = [NSMutableArray arrayWithArray:[urlResurlt componentsSeparatedByString:@"+"]];
if (self.mImgUrls.count >= 2) {
[self.mImgUrls removeLastObject];
}
// urlResurlt 就是獲取到得所有圖片的url的拼接;mImgUrls就是所有Url的數組
// 添加圖片可點擊js
[self.webView stringByEvaluatingJavaScriptFromString:@"function registerImageClickAction(){\
var imgs = document.getElementsByTagName('img');\
for (var i = 0; i < imgs.length; ++i){\
img = imgs[i];\
img.onclick = function(){\
window.location.href = 'image-preview:' + this.src}\
}\
}"];
[self.webView stringByEvaluatingJavaScriptFromString:@"registerImageClickAction();"];
}
Swift
func webViewDidFinishLoad(webView: UIWebView) {
var jsGetImages = "function getImages(){";
jsGetImages += "var objs = document.getElementsByTagName(\"img\");";
jsGetImages += "var imgScr = '';";
jsGetImages += "for (var i = 0; i < objs.length; ++i){";
jsGetImages += "imgScr = imgScr + objs[i].src + '+';";
jsGetImages += "};";
jsGetImages += "return imgScr;";
jsGetImages += "};";
webView.stringByEvaluatingJavaScriptFromString(jsGetImages);
let urlResurlt = webView.stringByEvaluatingJavaScriptFromString("getImages()");
mImgUrls = (urlResurlt?.componentsSeparatedByString("+"))!;
if (mImgUrls.count >= 2) {
mImgUrls.removeLast();
}
// urlResurlt 就是獲取到得所有圖片的url的拼接;mImgUrls就是所有Url的數組
// 添加圖片可點擊js
var jsRegisterImageClickAction = "function registerImageClickAction(){";
jsRegisterImageClickAction += "var imgs = document.getElementsByTagName('img');";
jsRegisterImageClickAction += "for (var i = 0; i < imgs.length; ++i){";
jsRegisterImageClickAction += "img = imgs[i];";
jsRegisterImageClickAction += "img.onclick = function(){";
jsRegisterImageClickAction += "window.location.href = 'image-preview:' + this.src}";
jsRegisterImageClickAction += "}";
jsRegisterImageClickAction += "}";
webView.stringByEvaluatingJavaScriptFromString("registerImageClickAction();");
}
寫在最后
其實以前總覺得手機原生的 SDK 做出來的頁面更精致,而加載的頁面怎么也不會超過原生的。而且 iOS 8.0 前的 UIWebView 你們都懂的,雖然有大神的各種優化方案但是還是不盡人意……可是隨著時間的演變,Web 與 App 交互已經成為主流了。以前沒怎么研究過這類交互以及 JS,不過JS 學起來還是很快的,語法也很簡單,經過了這次之后我也對 JS 產生了不小的興趣,以后有機會的話也會繼續深入的。
來自:http://www.jianshu.com/p/5af39a34f45d