iOS webView 與 JS 的交互

iOS 的webView分為兩類, 一類是 UIKit框架下的 UIWebView , 另一類是 WebKit框架下的 WKWebView. UIWebView 與JS 的交互是依賴于JavaScriptCore(iOS7.0),而WKWebView則是iOS8.0發(fā)布的.so 如果項目不需要兼容iOS8.0以下的版本則使用WKWebView即可.本文將介紹UIWebView WKWebView 與JS的交互.

一.UIWebView

對于UIWebView大家都不陌生,網(wǎng)頁或富文本加載都能用到,這里對于UIWebView基本的用法就不進行介紹了.UIWebView與JS的交互方面主要是依賴于JavaScriptCore類庫,下面簡單的介紹下這個類庫.
<JavaScriptCore/JavaScriptCore.h> Command+左鍵 進入

JavaScriptCore的類和協(xié)議.png
  1. JSContext ??????為JavaScript提供運行環(huán)境
  2. JSValue ??????? 是JavaScript和Object-C之間互換的橋梁
  3. JSManagedValue ?? 為開發(fā)人員提供對象內(nèi)存管理
  4. JSVirtualMachine ?? 為JavaScript的運行提供了底層資源
  5. JSExport ?????? 作為JavaScript和Objective-C兩種語言的互通協(xié)議篮赢。
在UIWebView中,native與JS的交互其實就是通過攔截request的方式實現(xiàn)的. 主要有兩種方法, 協(xié)議 和 Blcok.
協(xié)議(JSExport)

JSExport中沒有約定任何的方法址晕,連可選的(@optional)都沒有叛赚,但是所有繼承了該協(xié)議(@protocol)的協(xié)議(注意不是Objective-C的類(@interface))中定義的方法,都可以在JSContext中被使用。JSExport協(xié)議詳細講解

Native中的代碼

/**
 *  制定協(xié)議(遵循 JSExport協(xié)議)
 */

@protocol JSObjcProtocol <JSExport>

/**
 *  商品詳情頁面返回按鈕
 */
- (void)goBack;

/**
 *  購物車界面編輯按鈕
 */
- (void)edit;

/**
 *  支付訂單界面確認支付按鈕 以及三種傳值方式
 */
//javascript:App.pay(payInfo);
- (void)pay:(NSString *)payInfo;

//App.payInfoUserIDWithUserName("用戶ID","用戶名字");
- (void)payInfoUserID:(NSString *)userid withUserName:(NSString *)userName;

//App.payInfoOrder("訂單號","支付方式");
JSExportAs(payInfoOrder, - (void)payInfoOrder:(NSString *)order withPayType:(NSString *)type);

@nd

//控制器遵守協(xié)議之后, 協(xié)議中的方法都需要實現(xiàn).
@interface UIWebViewController ()<UIWebViewDelegate,JSObjcProtocol>
@property (strong, nonatomic) IBOutlet UIWebView *webView;
//為JavaScript提供運行環(huán)境
@property (strong ,nonatomic) JSContext *jsContext;
@end
- (void)webViewDidFinishLoad:(UIWebView *)webView{
    //獲取當(dāng)前加載完成網(wǎng)頁的上下文環(huán)境
    self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    //將設(shè)置的標(biāo)識符注入 由控制器接收
    self.jsContext[@"App"] = self;
    //異常處理
    self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
        context.exception = exceptionValue;
        NSLog(@"異常信息:%@", exceptionValue);
    };
}

這里解釋下 self.jsContext[@"App"] = self;
對應(yīng)JS中的代碼

<body class="by">
<div class="ftop">
 <a class="back" onClick="javascript:history.back(-1);">返回</a>
    <h2>購物車</h2>
    <a class="showde">編輯</a>
</div>
<script>
 $(function(){
      $(".showde").click(function(){
         App.edit();
       })
 })
</script>
</body>

這是UIWebView與JS交互的關(guān)鍵所在, JavaScriptCore框架會通過class_copyProtocolList方法找到類所遵循的協(xié)議碌上,然后再對每個協(xié)議通過protocol_copyProtocolList檢查它是否遵循JSExport協(xié)議進而將方法反映到JavaScript之中掀鹅。直白的說就是 調(diào)用本地方法之后從協(xié)議中取出相對應(yīng)的方法在JavaScript中調(diào)用.

關(guān)于異常處理:在JSContext中執(zhí)行的JavaScript如果出現(xiàn)異常,只會被JSContext捕獲并存儲在exception屬性上次屠,而不會向外拋出园匹。時時刻刻檢查JSContext對象的exception是否不為nil顯然是不合適,更合理的方式是給JSContext對象設(shè)置exceptionHandler劫灶,它接受的是^(JSContext *context, JSValue *exceptionValue)形式的Block裸违。其默認值就是將傳入的exceptionValue賦給傳入的context的exception屬性.
iOS7新JavaScriptCore框架入門介紹

Block

可以從一定程度上解決JSContext引用環(huán)境變量與控制器self的循環(huán)引用問題
Native中的代碼

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    //獲取網(wǎng)頁上下文環(huán)境
    self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    __weak typeof(self)  weakSelf = self;
    //Block可以傳入JSContext中當(dāng)做JavaScript的方法使用
    self.context[@"goBack"] = ^() {
        NSLog(@"點擊返回按鈕");
        dispatch_async(dispatch_get_main_queue(), ^{
            [weakSelf.navigationController popViewControllerAnimated:YES];
        });
    };
    
    self.context[@"goShopping"] = ^() {
        NSLog(@"點擊購物車按鈕");
        //獲取參數(shù)列表
        NSArray *args = [JSContext currentArguments];
        for (JSValue *jsVal in args) {
            NSLog(@"%@", jsVal);
        }
        //獲取當(dāng)前調(diào)用該方法的對象
        JSValue *this = [JSContext currentThis];
        NSLog(@"this: %@",this);

        [weakSelf goTo];
    };
}

- (void)goTo{
    //將參數(shù)傳進去來調(diào)用JS的方法
    [self.context evaluateScript:@"go('商品3')"];
}

JS中的代碼

<body>
<div class="ftop">
 <a class="back" onclick="goBack()">返回</a>
    <h2>商品詳情</h2>
    <a class="comea" onclick="goShopping(['商品1','商品2'])">購物車</a>
</div>

<script>
    function go(info){
        alert(info);
    }
</script>
</body>

二.WKWebView

適配iOS8.0,相較于UIWebView,性能功能方面都有了很大的提升,如果項目不需要適配iOS7的話可以直接使用WKWebView.
簡單介紹一下代理協(xié)議:

  1. WKNavigationDelegate: UIWebViewDelegate的功能都有,而且添加了頁面導(dǎo)航和網(wǎng)頁加載進度的監(jiān)聽
  2. WKUIDelegate: 監(jiān)聽JS方法中的alert、confirm本昏、prompt
  3. WKScriptMessageHandler: JS交互相關(guān)
@interface WKWebViewController ()<WKNavigationDelegate,WKUIDelegate,WKScriptMessageHandler>
@property (strong ,nonatomic) WKWebView *webView;
@end
- (void)viewDidLoad {
    [super viewDidLoad];
  
    //初始化
    //_webView = [[WKWebView alloc]initWithFrame:self.view.bounds];
    
    //帶有設(shè)置選項的初始化
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
    _webView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:configuration];
    
    //設(shè)置代理
    _webView.navigationDelegate = self;
    _webView.UIDelegate = self;
    
    //加載本地的HTML
    NSURL *url = [[NSBundle mainBundle] URLForResource:@"shopsDetail_WKWebView" withExtension:@"html"];
    [_webView loadRequest:[NSURLRequest requestWithURL:url]];
    
    [self.view addSubview:_webView];
    
    //添加一個名稱,可以在JS中通過這個名稱發(fā)送消息
    [[_webView configuration].userContentController addScriptMessageHandler:self name:@"goBackApp"];
    [[_webView configuration].userContentController addScriptMessageHandler:self name:@"goShoppingApp"];
}
/**
 *  WKScriptMessageHandler協(xié)議中必須要實現(xiàn)的方法
    用來接收JS端通過window.webkit.messageHandlers.{Name}.postMessage()方法發(fā)送的消息并做相應(yīng)處理
 */
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
    NSLog(@"方法名 %@供汛,傳回參數(shù) %@",message.name,message.body);
    
    //根據(jù)方法名來區(qū)分
    if ([message.name isEqual:@"goBackApp"]) {
        [self.navigationController popViewControllerAnimated:YES];
        
    }else if ([message.name isEqual:@"goShoppingApp"]){
        //調(diào)用JS中的方法并將參數(shù)傳遞到JS中
        [self.webView evaluateJavaScript:@"altertFromOC('商品1,商品2')" completionHandler:nil];
    }
}

JS中代碼

<body>
<div class="ftop">
 <a class="back" onclick="goBack()">返回</a>
    <h2>商品詳情</h2>
    <a onclick = "goShopping()">購物車</a>
</div>

<script>
    //接收Native發(fā)來的消息
    function altertFromOC(shops){
        alert(shops);
    }
    
    function goBack() {
        將goBackApp發(fā)送Native
        window.webkit.messageHandlers.goBackApp.postMessage(null);
    }

    function goShopping(){
         將goBackApp發(fā)送Native中,并攜帶參數(shù){shopsName:'商品名',shopsID:'商品ID'}
         window.webkit.messageHandlers.goShoppingApp.postMessage({shopsName:'商品名',shopsID:'商品ID'})
    }
</script>
</body>

在WKWebView中,網(wǎng)頁自身的alert、confirm涌穆、prompt等方法是被攔截的,不論是否有WKUIDelegate.為了能將傳遞至JS的參數(shù)顯示出來,這里調(diào)用WKUIDelegate的代理方法.

/**
 *  WKUIDelegate 代理方法
    用來監(jiān)聽webView中的alert方法
 *
 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    //調(diào)用原生的alert方法
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提醒" message:message preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }]];
    [self presentViewController:alert animated:YES completion:NULL];
}

//用來監(jiān)聽webView中的confirm方法
//- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;

//用來監(jiān)聽webView中的prompt方法
//- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;

Ok,交互方面大致就這些內(nèi)容,關(guān)于WKWebView的代理方法還有很多,用起來也很方便,這里就沒有寫了,畢竟主題是關(guān)于交互的.
代碼在這里

References:

  1. http://www.cocoachina.com/ios/20140415/8167.html
  2. http://blog.iderzheng.com/introduction-to-ios7-javascriptcore-framework/
  3. http://mp.weixin.qq.com/s__biz=MzIzMzA4NjA5Mw==&mid=400327803&idx=1&sn=2a09fa94dd605a9f03bbc16f998e5717#rd
  4. http://www.reibang.com/p/f896d73c670a
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末怔昨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子宿稀,更是在濱河造成了極大的恐慌趁舀,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祝沸,死亡現(xiàn)場離奇詭異矮烹,居然都是意外死亡,警方通過查閱死者的電腦和手機罩锐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門奉狈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人唯欣,你說我怎么就攤上這事嘹吨。” “怎么了境氢?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵蟀拷,是天一觀的道長。 經(jīng)常有香客問我萍聊,道長问芬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任寿桨,我火速辦了婚禮此衅,結(jié)果婚禮上强戴,老公的妹妹穿的比我還像新娘。我一直安慰自己挡鞍,他們只是感情好骑歹,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著墨微,像睡著了一般道媚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上翘县,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天最域,我揣著相機與錄音,去河邊找鬼锈麸。 笑死镀脂,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的忘伞。 我是一名探鬼主播薄翅,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼虑省!你這毒婦竟也來了匿刮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤探颈,失蹤者是張志新(化名)和其女友劉穎熟丸,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伪节,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡光羞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了怀大。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纱兑。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖化借,靈堂內(nèi)的尸體忽然破棺而出潜慎,到底是詐尸還是另有隱情,我是刑警寧澤蓖康,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布铐炫,位于F島的核電站,受9級特大地震影響蒜焊,放射性物質(zhì)發(fā)生泄漏倒信。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一泳梆、第九天 我趴在偏房一處隱蔽的房頂上張望鳖悠。 院中可真熱鬧榜掌,春花似錦、人聲如沸乘综。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘾带。三九已至鼠哥,卻和暖如春熟菲,著一層夾襖步出監(jiān)牢的瞬間看政,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工抄罕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留允蚣,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓呆贿,卻偏偏與公主長得像嚷兔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子做入,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 隨著H5技術(shù)的興起冒晰,在iOS開發(fā)過程中,難免會遇到原生應(yīng)用需要和H5頁面交互的問題竟块。其中會涉及方法調(diào)用及參數(shù)傳值等...
    Chris_js閱讀 3,060評論 1 8
  • 第一部分:webview與Js的交互 第二部分:設(shè)定cookie 第三部分:修改header頭 一.webview...
    指尖的跳動閱讀 1,215評論 0 0
  • 最近整理了一下原生與H5之間的交互方式浪秘,簡單的做個總結(jié)蒋情。OC端與JS的交互,大致有這幾種:攔截協(xié)議耸携、JavaScr...
    談Xx閱讀 31,102評論 41 75
  • 前言 Web 頁面中的 JS 與 iOS Native 如何交互是每個 iOS 猿必須掌握的技能棵癣。而說到 Nati...
    幽城88閱讀 2,195評論 1 8
  • 跟原生開發(fā)相比,H5的開發(fā)相對來一個成熟的框架和團隊來講在開發(fā)速度和開發(fā)效率上有著比原生很大的優(yōu)勢夺衍,至少不用等待審...
    大沖哥閱讀 1,828評論 0 7