iOS中js調(diào)用oc獲取返回值(WKWebView)

前言

最近公司的APP需要進(jìn)行hybrid模式的開發(fā),即native和h5聯(lián)合開發(fā)。此時(shí)前端工程師提到了一個(gè)需求,由前端調(diào)用native進(jìn)行操作以及獲取返回值。這樣可以保證native只有一份代碼,h5不用指定方法和native進(jìn)行確認(rèn)。為了實(shí)現(xiàn)這個(gè)需求,從網(wǎng)上的給出的解決辦法來看,一種是使用原生的類和庫(kù)方法,另外一種是使用第三方庫(kù)。本文主要介紹使用原生類庫(kù)進(jìn)行操作。

實(shí)現(xiàn)效果(demo)

demo實(shí)現(xiàn)效果

在實(shí)現(xiàn)效果圖中,當(dāng)前view是加載了wkwebview的實(shí)例,并且wkwebview加載了本地的html,界面上jsInvokeOC和invokeOCGetCookie是兩個(gè)button。jsInvokeOC點(diǎn)擊之后會(huì)調(diào)用native方法,而native方法又調(diào)用了js方法將結(jié)果異步傳給html用于顯示,也可以不進(jìn)行顯示直接在native中做相當(dāng)?shù)膭?dòng)作,比如返回上一級(jí)。下面的invokeOCGetCookie是直接調(diào)用的native,由native返回同步顯示。

實(shí)現(xiàn)

  1. 首先貼一下html和js代碼,這樣后面好敘述。
    test.html
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>WebViewOCInvokeDemo</title>
        <style>
            *{
                font-size: 50px;
            }
        .btn{height:80px; width:60%; padding: 0px 30px; background-color: #0071E7; border: solid 1px #0071E7; border-radius:5px; font-size: 1em; color: white}
            </style>
        <script src="test.js"></script>
        
    </head>
    
    <body>
        <div>
            <button class="btn" type="button" onclick="jsInvokeOC()">jsInvokeOC</button>
            <br />
            <button class="btn" type="button" onclick="getCookie()">invokeOCGetCookie</button>
        </div>
        <br/>
        <div id="cookie">Cookie's value</div>
        <br/>
        <div id="response">response's value</div>
    </body>
</html>

test.js

function jsInvokeOC() {
    window.webkit.messageHandlers.jsInvokeOCMethod.postMessage('Javascript invoke OC');
}

function getCookie() {
    var cookie = window.prompt("getCookie");
    
    document.getElementById('cookie').innerText = "cookie: " + cookie;
}

function response2JS(response) {
    document.getElementById('response').innerText = "resp: " + response;
}
  1. 對(duì)于第一個(gè)按鈕觸發(fā)的方法是由下面這行代碼調(diào)用的。
window.webkit.messageHandlers.jsInvokeOCMethod.postMessage('Javascript invoke OC');

該方法的的原型是:
window.webkit.messageHandlers.<方法名>.postMessage(<消息內(nèi)容>);
<方法名>是和oc之間商量好的方法名,用于oc判斷;
<消息內(nèi)容>用于發(fā)給oc的消息內(nèi)容字符串。

這種方法的實(shí)現(xiàn)需要WKWebViewConfiguration的WKUserContentController添加對(duì)<方法名>的消息監(jiān)聽,觸發(fā)后在代理方法:

- (void)userContentController:(WKUserContentController *)userContentController
      didReceiveScriptMessage:(WKScriptMessage *)message;

中通過判斷message.name獲取<方法名>,通過message.body獲取<消息內(nèi)容>。
實(shí)現(xiàn)代碼如下:
(1)配置WKUserContentController。

- (WKWebView*)webView {
    if (!_webView) {
        WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc]
                                                 init];
        
        _webView = [[WKWebView alloc] initWithFrame: CGRectZero
                                      configuration: configuration];
        
        WKUserContentController* userCC = _webView.configuration.userContentController;
        
        [userCC addScriptMessageHandler: self
                                   name: @"jsInvokeOCMethod"];
        
        _webView.UIDelegate = self;
        _webView.navigationDelegate = self;
    }
    
    return _webView;
}

(2)實(shí)現(xiàn)回調(diào)方法。

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController
      didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([message.name isEqualToString: @"jsInvokeOCMethod"]) {
        NSLog(@"MessageBody: %@", message.body);
        
        // async return value
        [self.webView evaluateJavaScript: @"response2JS('Hello return')"
           completionHandler:^(id response, NSError * error) {
               NSLog(@"response: %@, \nerror: %@", response, error);
           }];
    }
}

實(shí)現(xiàn)回調(diào)方法中調(diào)用了evaluateJavaScript是為了異步調(diào)用js方法來返回值,其中response2JS('Hello return')是js方法,在文件test.js中。
當(dāng)然此處可以不用異步返回,直接進(jìn)行某種操作,比如oc中的彈框,popViewController或者是pushViewController操作。

  1. 第二個(gè)按鈕的觸發(fā)實(shí)現(xiàn)操作如下。
    第二個(gè)按鈕直接觸發(fā)的語句為:
var cookie = window.prompt("getCookie");

直接獲取cookie,prompt方法會(huì)直接被oc的WKUIDelegate代理中的runJavaScriptTextInputPanelWithPrompt代理方法所捕獲到。
prompt有兩個(gè)參數(shù),第一個(gè)是prompt,第二個(gè)defaulttext,分別和代理方法中相對(duì)應(yīng)。
代理方法實(shí)現(xiàn)如下:

#pragma mark - WKUIDelegate delegate method
- (void)webView:(WKWebView *)webView
runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt
    defaultText:(NSString *)defaultText
initiatedByFrame:(WKFrameInfo *)frame
completionHandler:(void (^)(NSString * _Nullable))completionHandler {
    if (prompt) {
        if ([prompt isEqualToString: @"getCookie"]) {
            completionHandler(@"eba7392f-f754-4a56-9c22-aedf3ffb79d8");
        }
    }
}

這樣由js調(diào)用oc便實(shí)現(xiàn)了。

結(jié)束語

本文沒有使用第三方庫(kù)的實(shí)現(xiàn),只是調(diào)研了原生的類庫(kù)的使用方式。其實(shí)可以研究一下第三方類庫(kù)wkwebviewjavascriptbridge,這樣可以保證前端只寫一份代碼,可以在iOS和Android上共同使用。在以后的時(shí)間里面,筆者會(huì)繼續(xù)研究第三方類庫(kù)的使用,并在簡(jiǎn)書上進(jìn)行分享。
目前代碼已經(jīng)托管至github上。

https://github.com/njim3/WebViewOCInvokeDemo.git
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容