iOS下 JS 與OC 互相調(diào)用(一) - UIWebView 攔截 URL

序言

在實際開發(fā)中,我們避免不了需要和 UIWebView 打交道,這就涉及到 JS 與原生 OC 的交互,今天抽空總結(jié)一下 JS 與原生交互使用 UIWebView 攔截 URL 的方式。

JS 調(diào)用原生 OC

我們可以利用 JS 發(fā)起一個假的 URL 請求,然后利用 UIWebView 的代理方法攔截這次請求,然后再做相應(yīng)的處理。

參考網(wǎng)上例子,寫了一個簡單的 HTML 網(wǎng)頁和一個按鈕點擊事件用來和原生 OC 交互,HTML代碼如下:

<html>
    <header>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <script type="text/javascript">
            
            function showAlert(message){
                asyncAlert(message);
            }
        
            function asyncAlert(content) {
                setTimeout(function(){
                           alert(content);
                           },1);
            }
        
            function loadURL(url) {
                var iFrame;
                iFrame = document.createElement("iframe");
                iFrame.setAttribute("src", url);
                iFrame.setAttribute("style", "display:none;");
                iFrame.setAttribute("height", "0px");
                iFrame.setAttribute("width", "0px");
                iFrame.setAttribute("frameborder", "0");
                document.body.appendChild(iFrame);
                // 發(fā)起請求后這個 iFrame 就沒用了,所以把它從 dom 上移除掉
                iFrame.parentNode.removeChild(iFrame);
                iFrame = null;
            }
            function firstClick() {
                loadURL("firstClick://shareClick?title=分享的標(biāo)題&content=分享的內(nèi)容&url=鏈接地址&imagePath=圖片地址");
            }
        </script>
    </header>
    
    <body style="width:100%; height:100%;">
        <h2> 這里是第一種方式 </h2>
        <br/>
        <br/>
        <button type="button" onclick="firstClick()">Click Me!</button>
        
    </body>
</html>

雖然 HTML 內(nèi)容比較少,還是有很多學(xué)問的

1.為什么定義一個loadURL方法,不直接使用window.location.href?
答:因為如果當(dāng)前網(wǎng)頁正在使用window.location.href加載網(wǎng)頁的同時,調(diào)用window.location.href去調(diào)用 OC 原生方法,會導(dǎo)致加載網(wǎng)頁的操作被取消掉。同樣的,如果連續(xù)使用window.location.href執(zhí)行兩次 OC 原生調(diào)用,也有可能導(dǎo)致第一次的操作被取消掉。所以我們使用自定義的loadURL,來避免這個問題。loadURL的實現(xiàn)來自關(guān)于UIWebView和PhoneGap的總結(jié)一文。
2.為什么 loadURL 中的鏈接,使用統(tǒng)一的 scheme?
答:便于在 OC 中做攔截處理,減少在 JS 中調(diào)用一些 OC 沒有實現(xiàn)的方法時,webView 做跳轉(zhuǎn)。我再 OC 攔截 URL 時,根據(jù) scheme即(firstclick)來區(qū)分是調(diào)用原生的方法還是正常的網(wǎng)頁跳轉(zhuǎn)。然后根據(jù)host(即//后面的部分shareClick)來區(qū)分執(zhí)行什么操作。
3.為什么自定義一個asyncAlert方法?
答:因為有的 JS 調(diào)用是需要 OC 返回結(jié)果到 JS 的。
stringByEvaluatingJavaScriptFromString是一個同步方法,會等待 js 方法執(zhí)行完成。而彈出的 alert 也會阻塞界面等待用戶響應(yīng),所以他們可能會造成死鎖。導(dǎo)致 alert 卡死界面。如果回調(diào)的 JS 是一個耗時操作,那么建議將耗時的操作也放入setTimeoutfunction中。

然后在項目的控制器中實現(xiàn) UIWebView 的代理方法

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSURL * url = [request URL];
    if ([[url scheme] isEqualToString:@"firstclick"]) {  // firstClick://shareClick?title=分享的標(biāo)題&content=分享的內(nèi)容&url=鏈接地址&imagePath=圖片地址
        NSArray *params = [url.query componentsSeparatedByString:@"&"];
        
        NSMutableDictionary *tempDict = [NSMutableDictionary dictionary];
        NSMutableString *strM = [NSMutableString string];
        for (NSString *paramStr in params) {
            NSArray *dictArray = [paramStr componentsSeparatedByString:@"="];
            if (dictArray.count > 1) {
                NSString *decodeValue = [dictArray[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
                decodeValue = [decodeValue stringByRemovingPercentEncoding];
                [tempDict setObject:decodeValue forKey:dictArray[0]];
                [strM appendString:decodeValue];
            }
        }
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"這是OC原生的彈出窗" message:strM delegate:self cancelButtonTitle:@"收到" otherButtonTitles:nil];
        [alertView show];
        NSLog(@"tempDic:%@",tempDict);
        return NO;
    }
    return YES;
}

1.JS中的firstClick,在攔截到的url scheme全都被轉(zhuǎn)化為小寫。
2.html 中需要設(shè)置編碼,否則中文參數(shù)可能會出現(xiàn)編碼問題。
3.JS用打開一個iFrame的方式替代直接用document.location的方式,以避免多次請求,被替換覆蓋的問題。

OC 調(diào)用 JS

在 OC 原生中

NSString *jsStr = [NSString stringWithFormat:@"showAlert('%@')",@"這里是JS中alert彈出的message"];
[self.webView stringByEvaluatingJavaScriptFromString:jsStr];

注意:該方法會同步返回一個字符串,因此是一個同步方法,可能會阻塞主線程。

在 html 文件中

function showAlert(message) {
  alert(message);
}

早期的JS與原生交互的開源庫很多都是用得這種方式來實現(xiàn)的,例如:PhoneGap、WebViewJavascriptBridge。關(guān)于這種方式調(diào)用OC方法,唐巧早期有篇文章有過介紹:
關(guān)于UIWebView和PhoneGap的總結(jié)

效果如下:

js_oc_intercept_url.gif

項目連接地址


更多 JS 與 OC 交互文章請看下面
iOS下 JS 與OC 互相調(diào)用(二) - JavaScriptCore
iOS 下 JS 與 OC 互相調(diào)用(三) - WKWebView 攔截 URL
iOS下JS與OC互相調(diào)用(四)-MessageHandler
iOS下 JS 與 OC 互相調(diào)用(五) - UIWebView+WebViewJavascriptBridge
iOS下 JS 與 OC 互相調(diào)用(六) - WKWebView+WKWebViewJavascriptBridge

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

相關(guān)閱讀更多精彩內(nèi)容

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