iOS JS 和 OC交互 / JS 和 native 相互調用


現在app 上越來越多需求是通過UIWebView 來展示html 或者 html5的內容, js 和 native OC代碼交互 就非常常見了.

js 調用 native  OC代碼

第一種機制

(1)最常用的是 利用 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 這個UIWebView 代理方法里攔截 JS 發送的請求,如果是約定的請求 那么就觸發本地該執行的OC方法

我遇到常用的兩種場景:

(1.1)頁面緩沖加載過程中 該shouldStartLoadWithRequest:就可攔截多個請求,查找約定的關鍵字,鎖定約定請求即可觸發本地OC

(1.2)頁面有按鈕類別的焦點區域, 點擊觸發請求,通過 shouldStartLoadWithRequest: 對請求進行判斷 ,觸發本地OC

     舉例:html 分享按鈕

<html>
    <header>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <script type="text/javascript">function shareClick() {
                loadURL("iOS//:shareAction");//這個鏈接對應 下面iOS方法中同一鏈接的關鍵字
            }
        </script>
    </header>

    <body>
        <h2> 點擊webView上面的按鈕 觸發本地 OC 分享方法 </h2>
        <button type="button" onclick="shareClick()">按鈕名稱</button>
    </body>
</html>

 

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSString *requestUrl = request.URL.absoluteString;
DLog(@"請求URL:%@",requestUrl);
//判斷是商品
if(![NSString isEmpty:requestUrl] && [requestUrl hasPrefix:@"hfmall://"]) {//約定 按請求前綴 是"hfmall://"來判斷是商品 甚至可以從請求鏈接里面截取有效參數 如果頻繁處理並且幾個參數或者說有特殊字符,應該考慮用json進行64位編拼在鏈接上傳過來 NSRange range = [requestUrl rangeOfString:@"hfmall://"]; if (range.length > 0) { NSString *shangpinId = [requestUrl substringFromIndex:range.length]; GoodsDetailViewController *detailVC = [[GoodsDetailViewController alloc] init]; detailVC.shangpin_id = shangpinId; [self.navigationController pushViewController:detailVC animated:YES]; return NO; //執行本地代碼 返回NO } }
//點擊UIWebView頁面上分享按鈕 執行本地OC 分享方法
//是文章分享
   NSRange range1 = [requestUrl rangeOfString:@"ios//:shareAction"];
if (range1.length != 0) {
[self share]; //執行分享方法
return NO;
}
//TODO:其他情況攔截判斷
return YES;//無有效需要攔截的鏈接 走系統默認方法
}

 

第二種機制

JavaScripCore

該框架 在 iOS7開始出現,可以實現 js 和 native OC原生交互,添加頭文件#import <JavaScriptCore/JavaScriptCore.h>

 

[webView loadRequest:request];請求開始或者請求完成后獲取js上下文

(1)js 自由選擇時機調用native端 OC 代碼

html端

<html>
    <header>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <script type="text/javascript">
            function secondClick() { iosHideBottomBar('參數1','參數2');//和 OC代碼中標記紅色背景關鍵名稱一樣 即可識別 }
</script>
</header> <body> <h2> js 自由選擇時機調用native端 OC 代碼 </h2> <button type="button" onclick="secondClick()">按鈕名稱</button> </body> </html>

 

//獲取js上下文
JSContext
*context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

  context[@"iosHideBottomBar"] = ^() { // @"iosHideBottomBar"為和js 約定執行的方法名,這樣js 可以自由選擇時機調用native端 OC 代碼

        DLog(@"%@",[NSThread currentThread]);//打印當前線程

     NSArray *args = [JSContext currentArguments];//回調js 方法iosHideBottomBar傳遞的參數

        for (JSValue *jsVal in args) {

            DLog(@"%@", jsVal.toString);

        }

        __block JSContext *contextObject = context;

       dispatch_async(dispatch_get_main_queue(), ^{

            self.isShowLikeFooter = NO;

            [self handleHiddenBottomBar];

            NSString *jsString=@"xxxxx('如果需要傳遞返回值')"; //准備執行的js代碼 按約定方法xxxxx回傳返回值

            [contextObject evaluateScript:jsString];

        });

  };

 //該閉包是在非主線程中得到的回調,如果需要處理UI要在主線程更新 當前測試環境 為 XCode 8.0 模擬器 5s 8.3 打印結果如下

/**
2016-11-11 17:05:54.447 dailylife[4163:181210] <NSThread: 0x7ff68cc2fd90>{number = 7, name = (null)}

(lldb) po [NSThread currentThread]

<NSThread: 0x7ff68a423150>{number = 1, name = main}

**/

 小節:

兩種機制不同:

a.  前者需要 不斷對js 發起的每一條請求進行過濾判斷,再執行早已定義好的OC方法,因為無法預知具體需要觸發本地OC方法時機,

b.  后者 js 和 OC約定同一個方法名做識別關鍵字iosHideBottomBar 年 在JSContext獲取webView js上下文,告訴js 本地有這個同樣方法名的OC方法,你可以隨時調用(子線程中).

體驗下來,發現 后者這種 webView 控制調用更合理,在需要的時候調用.  但是現在好多第三方 用的都是前者,攔截url判斷的方法,比如"有贊"就是,所以兩種方法 我們都應該掌握,在需要的的時候選擇相對更優的方式處理實現

native OC 調用 js

調用時機是應該是 webFinishLoad后, 

(1)stringByEvaluatingJavaScriptFromString : 我常用這個方法去獲取 webView標題

NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];

@"document.title" 傳遞給js  (js call native) 是異步的

當js 返回的 title字符串 (native call  js) 是同步得到的

stringByEvaluatingJavaScriptFromString 方法應該在主線程中執行,

這個方法也可以oc js相互傳遞參數 

這個方法調用時機:應該在webview 請求完成后再調用 js 方法,這里才能用stringByEvaluatingJavaScriptFromString,因為

要等頁面加載完,頁面沒加載完就相當於有些對象不一定創建成功,那么用js時候就容易找不到對象

(2)

//創建對象 context 獲取 js上下文
JSContext *context=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];      \
NSString
*jsString=@"iosHideBottomBar('參數1','參數2')"; //准備執行的js代碼 並向iOS 傳遞參數1 參數2 [context evaluateScript:jsString];//通過oc方法調用js的 iosHideBottomBar

 

其他情況:

我還特意考慮了 相互需要返回值的可能

在具體上面方法舉例,使用方法名時候 有特意強調參數,和返回值的情況不再贅述

參考:

http://blog.devtang.com/2012/03/24/talk-about-uiwebview-and-phonegap/

http://www.jianshu.com/p/d19689e0ed83

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM