【重要】專爲你定製的 JS/Natvie 交互專題

2018 年距離第一代 iOS 系統發佈(2007 年)已通過去 11 年,這 11 年中移動端日益成熟,Web 端的時代逐步轉移到了移動端,天然而然 Web 端的開發技術棧開始逐步移動到移動端。這就引起一個尷尬的局面,Web 端的同窗不瞭解移動端的開發知識,移動端不瞭解 Web 端的開發知識。爲了解決這個問題,知識小集打算從基礎出發,介紹 JavaScript 與 iOS 交互時用到的技術點,好比 JavaScriptCore、JavaScript 基礎、JavaScriptCore 的實際使用場景(深度剖析 JSPatch 的實現)等。而今天這篇就是其中的一篇,主要介紹一個 Hybrid WebView 的實現。接下來咱們會把文章逐步發出來供咱們的讀者朋友參考,咱們初步定的目錄以下(若是你不想錯過咱們這個專題,關注咱們的公衆號【知識小集】吧,關於這個專題有什麼建議均可以經過公衆號告訴咱們):前端

  • 前言
  • JavaScript 基礎知識
  • JavaScript 進階
  • JavaScript-native 調試
  • 開啓本地 Webserver
  • WKWebView 概述
  • JavaScriptCore 總覽
  • JavaScript 與 ObjectiveC 間的類型轉換
  • JavaScript 與 ObjectiveC 通訊
  • ObjectiveC 與 JavaScript 通訊
  • 本身動手實現一個 Hybrid WebView
  • JSPatch 中的 JavaScriptCore
  • JSPatch 中的 Runtime
  • JSPatch 原理深度剖析
  • JSPatch 雜談
  • 讀 Aspects 理解 runtime

本身動手實現一個 Hybrid WebView

現在,端與 Web 頁的交互愈來愈頻繁,不少頁面都交給 Web 頁面來實現,而有些狀況下 Web 須要與端進行交互。面對這種需求,各類第三方庫源源不斷出現,而 WebViewJavascriptBridge 無疑是 star 最多的一個。其實目前在 iOS 開發當中,大多數都切換到了 WKWebView,且對 Web 的交互愈來愈重,因此不妨本身實現一個 Hybrid WebView 來知足本身的業務需求。一個 Hybrid WebView 最基本的應該知足雙方能夠自由通訊。git

  • WebView 上的事件能夠傳遞到端上;
  • WebView 能夠從端上獲取數據;
  • 端能夠監聽到 WebView 上發生的事件。

本文旨在說明一個 Hybrid WebView 須要的技術手段,因此打算從一個具體的需求出發,一步一步搭建一個 Hybrid WebView。大多數的文章只會講解端上如何實現,而本文會結合前端一塊講講兩端是如何實現的。github

需求說明

Web 頁面上有一張圖和一個保存按鈕,當點擊保存按鈕時會提示用戶是否須要保存圖片到相冊。若是保存成功,按鈕的標題將變爲已保存,不然標題爲保存到相冊。若是已保存,下次進入 Web 頁時顯示已保存。web

分析上面的需求,能夠拆分爲:後端

  • 頁面加載後,須要獲取圖片是否已經保存過,若是已保存,按鈕的標題爲「已保存」,不然爲「保存到相冊」;
  • 點擊按鈕須要提示用戶「是否須要保存圖片到相冊」,點擊「保存」執行保存操做。點擊取消將什麼也不作;
  • 保存成功,按鈕上的標題須要變爲「已保存」。

分析完上面具體需求後,轉換爲技術須要考慮的問題:bash

  • 頁面加載後,Web 頁能夠從端上獲取到圖片是否已經保存的狀態;
  • 點擊保存按鈕,須要在端上提示用戶,用戶點擊保存須要把圖片保存到相冊,這時須要獲取到當前顯示的圖片,也就是說須要把 Web 頁面中的數據傳遞到端;
  • 保存成功後須要修改 Web 頁面按鈕的標題。

先作一個 Web 頁面

總體頁面是如上圖所示。咱們逐步剖析是如何實現的。框架

在前面的章節中(這些章節後續會發出來),已經介紹了在 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 會在這個專題完成後一塊放出。

相關文章
相關標籤/搜索