2018 年距離第一代 iOS 系統發佈(2007 年)已通過去 11 年,這 11 年中移動端日益成熟,Web 端的時代逐步轉移到了移動端,天然而然 Web 端的開發技術棧開始逐步移動到移動端。這就引起一個尷尬的局面,Web 端的同窗不瞭解移動端的開發知識,移動端不瞭解 Web 端的開發知識。爲了解決這個問題,知識小集打算從基礎出發,介紹 JavaScript 與 iOS 交互時用到的技術點,好比 JavaScriptCore、JavaScript 基礎、JavaScriptCore 的實際使用場景(深度剖析 JSPatch 的實現)等。而今天這篇就是其中的一篇,主要介紹一個 Hybrid WebView 的實現。接下來咱們會把文章逐步發出來供咱們的讀者朋友參考,咱們初步定的目錄以下(若是你不想錯過咱們這個專題,關注咱們的公衆號【知識小集】吧,關於這個專題有什麼建議均可以經過公衆號告訴咱們):前端
現在,端與 Web 頁的交互愈來愈頻繁,不少頁面都交給 Web 頁面來實現,而有些狀況下 Web 須要與端進行交互。面對這種需求,各類第三方庫源源不斷出現,而 WebViewJavascriptBridge 無疑是 star 最多的一個。其實目前在 iOS 開發當中,大多數都切換到了 WKWebView,且對 Web 的交互愈來愈重,因此不妨本身實現一個 Hybrid WebView 來知足本身的業務需求。一個 Hybrid WebView 最基本的應該知足雙方能夠自由通訊。git
本文旨在說明一個 Hybrid WebView 須要的技術手段,因此打算從一個具體的需求出發,一步一步搭建一個 Hybrid WebView。大多數的文章只會講解端上如何實現,而本文會結合前端一塊講講兩端是如何實現的。github
Web 頁面上有一張圖和一個保存按鈕,當點擊保存按鈕時會提示用戶是否須要保存圖片到相冊。若是保存成功,按鈕的標題將變爲已保存,不然標題爲保存到相冊。若是已保存,下次進入 Web 頁時顯示已保存。web
分析上面的需求,能夠拆分爲:後端
分析完上面具體需求後,轉換爲技術須要考慮的問題:bash
總體頁面是如上圖所示。咱們逐步剖析是如何實現的。框架
在前面的章節中(這些章節後續會發出來),已經介紹了在 Web 頁面中執行 JavaScript 。能夠把一段 JavaScript 代碼嵌入到 HTML 中,這時在 HTML 中能夠直接調用 JavaScript 代碼,而 JavaScript 能夠經過 DOM 動態來操做 HTML 中的標籤,這樣既能夠達到動態修改 Web 頁。函數
Web與端通訊的JS代碼,這段代碼是嵌入在 HTML 中的。post
<script>
// 標記保存的狀態
var saved = false;
// 保存事件
function saveaction(){
if (saved) {
return;
}
alert("肯定要保存該圖片嗎?");
// 發送消息給客戶端 JS 中發送消息給 OC
var param = {url : "https://raw.githubusercontent.com/iOS-Tips/iOS-tech-set/master/images/qrcode.jpg"};
window.webkit.messageHandlers.JSBridge.postMessage(JSON.stringify(param));
};
// 保存成功後端會調用這個方法通知Web頁保存成功
function save_success(){
change_state(true);
};
// 修改是否已保存的狀態,修改按鈕標題
function change_state(issaved){
saved = issaved;
var button = document.getElementById('saveid');
if (issaved){
// 若是已經保存,修改按鈕的標題爲已保存,不然顯示 保存到相冊
button.innerText = "已保存";
} else {
button.innerText = "保存到相冊";
}
}
</script>
複製代碼
保存到相冊 按鈕,監聽點擊事件,當點擊按鈕後會調用 saveaction
函數。ui
<div id="saveid" class="save_button" onclick="saveaction()">保存到相冊</div>
複製代碼
而 saveaction
函數首先會發一個 alert("肯定要保存該圖片嗎?")
到端,端會執行 WKUIDelegate
代理方法,咱們在這個方法須要彈窗端內的提示框:
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"舒適提示" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"保存" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
self.isOKAction = YES;
completionHandler();
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
self.isOKAction = NO;
completionHandler();
}]];
[self presentViewController:alert animated:YES completion:nil];
}
複製代碼
當用戶點擊保存按鈕後,會保存圖片到相冊。因此客戶端須要拿到圖片的地址,這是須要給端發送圖片的地址。若是想給端發送一條消息,直接在 Web 頁經過 JavaScript 執行,其中 xxxx 是端與Web之間約定的名字。
window.webkit.messageHandlers.xxxx.postMessage(JSON.stringify(param))
複製代碼
而咱們此時定義的名字是 JSBridge
,當用戶點擊保存後,須要根據Web傳遞過來的 URL 保存圖片。
var param = {url : "https://raw.githubusercontent.com/iOS-Tips/iOS-tech-set/master/images/qrcode.jpg"};
window.webkit.messageHandlers.JSBridge.postMessage(JSON.stringify(param));
複製代碼
當端接收到 Web 發過來的消息後,會調用 WKScriptMessageHandler
的代理方法,在這個方法中咱們來下載圖片並保存到相冊:
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
if ([message.body isKindOfClass:[NSString class]]) {
if ([message.name isEqualToString:kScriptMsgName] && self.isOKAction) {
// 保存圖片
NSDictionary *msgInfo = [NSJSONSerialization JSONObjectWithData:[message.body dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:msgInfo[@"url"]]]];
if (image) {
UIImageWriteToSavedPhotosAlbum(image, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), nil);
}
}
}
}
複製代碼
當把圖片保存到相冊後,須要刷新 Web 頁面上的按鈕的標題,這時須要執行 Web 頁中已經定義好的 change_state
方法:
- (void)updateSaveState:(BOOL)isSave
{
NSString *script = isSave ? @"change_state(true);" : @"change_state(false);";
[self.webView evaluateJavaScript:script completionHandler:^(id _Nullable msg, NSError * _Nullable error) {}];
}
複製代碼
至此,咱們還剩下最後一件事沒有完成,當加載出 WebView 後,須要根據本地是否已經保存了圖片更新按鈕的標題,直接調用 updateSaveState
函數便可。
本文主要介紹一個 Hybrid WebView 如何實現,它僅僅是從一個具體的需求出發,而若是作一個通用 Hybrid WebView 框架須要兩端設計一種通訊規則。具體細節能夠參考味精的兩篇關於 Hybrid 的實踐 (從零收拾一個hybrid框架)。本文的 demo 會在這個專題完成後一塊放出。