OC與JS交互之WKWebView

上一篇文章我們使用了JavaScriptCore框架UIWebView的示例醒陆,iOS8蘋果偏愛HTML5溪椎,重構了UIWebVIew氛改,給我們帶來了WKWebView稍刀,使其性能撩独、穩(wěn)定性敞曹、功能大幅度提升,也更好的支持了HTML5的新特性综膀。這篇文章就們就拿WKWebView來小試牛刀澳迫,這也是我的學習過程。
我們先來看一下學習這個WKWebView的主要類別

閱讀目錄

  • 一剧劝、WKWebView Framework
  • 二橄登、WKWebView中的三個代理方法
  • 三、使用WKWebView重寫
  • 四讥此、后記

WKWebView的14個類與3個協(xié)議:

WKBackForwardList: 之前訪問過的 web 頁面的列表拢锹,可以通過后退和前進動作來訪問到。

WKBackForwardListItem: webview 中后退列表里的某一個網(wǎng)頁萄喳。

WKFrameInfo: 包含一個網(wǎng)頁的布局信息卒稳。

WKNavigation: 包含一個網(wǎng)頁的加載進度信息。

WKNavigationAction: 包含可能讓網(wǎng)頁導航變化的信息他巨,用于判斷是否做出導航變化展哭。

WKNavigationResponse: 包含可能讓網(wǎng)頁導航變化的返回內(nèi)容信息,用于判斷是否做出導航變化闻蛀。

WKPreferences: 概括一個 webview 的偏好設置。

WKProcessPool: 表示一個 web 內(nèi)容加載池您市。 

WKUserContentController: 提供使用 JavaScript post 信息和注射 script 的方法觉痛。

WKScriptMessage: 包含網(wǎng)頁發(fā)出的信息。

WKUserScript: 表示可以被網(wǎng)頁接受的用戶腳本茵休。 

WKWebViewConfiguration: 初始化 webview 的設置薪棒。

WKWindowFeatures: 指定加載新網(wǎng)頁時的窗口屬性。

WKWebsiteDataStore: 包含網(wǎng)頁數(shù)據(jù)存儲和查找榕莺。

 

WKNavigationDelegate: 提供了追蹤主窗口網(wǎng)頁加載過程和判斷主窗口和子窗口是否進行頁面加載新頁面的相關方法俐芯。

WKUIDelegate: 提供用原生控件顯示網(wǎng)頁的方法回調(diào)。

WKScriptMessageHandler: 提供從網(wǎng)頁中收消息的回調(diào)方法钉鸯。

二吧史、WKWebView中的三個代理方法

  1. WKNavigationDelegate
    該代理提供的方法,可以用來追蹤加載過程(頁面開始加載唠雕、加載完成贸营、加載失敗)岩睁、決定是否執(zhí)行跳轉(zhuǎn)钞脂。
// 頁面開始加載時調(diào)用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
// 當內(nèi)容開始返回時調(diào)用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
// 頁面加載完成之后調(diào)用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
// 頁面加載失敗時調(diào)用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;

頁面跳轉(zhuǎn)的代理方法有三種,分為(收到跳轉(zhuǎn)與決定是否跳轉(zhuǎn)兩種)

// 接收到服務器跳轉(zhuǎn)請求之后調(diào)用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
// 在收到響應后捕儒,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
// 在發(fā)送請求之前冰啃,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
  1. WKUIDelegate
    創(chuàng)建一個新的WKWebView
// 創(chuàng)建一個新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;

剩下三個代理方法全都是與界面彈出提示框相關的,針對于web界面的三種提示框(警告框、確認框阎毅、輸入框)分別對應三種代理方法焚刚。

// 界面彈出警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler;
// 界面彈出確認框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
// 界面彈出輸入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;
  1. WKScriptMessageHandler
    這個協(xié)議中包含一個必須實現(xiàn)的方法,這個方法是native與web端交互的關鍵净薛,它可以直接將接收到的JS腳本轉(zhuǎn)為OC或Swift對象汪榔。
// 從web界面中接收到一個腳本時調(diào)用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;

三、使用WKWebView

我這里加載的是本地的html肃拜,我先貼出html的代碼

<!DOCTYPE html>
<html>
<head>

    <meta charset="utf-8" http-equiv="Content-Type" content="text/html">

    <title>小紅帽</title>

    <style>
        *{
             font-size: 50px;
        }

        .div{
            align:"center";
         }
    
        .btn{
            height:80px; width:80%; padding: 0px 30px 0px 30px; background-color: #0071E7; border: solid 1px #0071E7; border-radius:5px; font-size: 1em; color: white
        }
    </style>

    <script>
        function clear(){
            document.getElementById('mobile').innerHTML=''
            document.getElementById('name').innerHTML=''
            document.getElementById('msg').innerHTML=''
        }
       //oc調(diào)用js的方法列表
       function alertMobile(){
               //這里已經(jīng)調(diào)用過來了 但是搞不明白為什么alert方法沒有響應
                //alert('我是上面的小黃 手機號是:13300001111')
             document.getElementById('mobile').innerHTML='我是上面的小黃 手機號是:13300001111'
       }

      function alertName(msg){
             document.getElementById('name').innerHTML='你好 ' + msg + ', 我也很高興見到你'
      }
      
      function alertSendMsg(num,msg){
        document.getElementById('msg').innerHTML='這是我的手機號:' + num + ',' + msg + '!!'
      }

      //JS響應方法列表
      function btnClick1(){
        window.webkit.messageHandlers.showMobile.postMessage(null)
        //window.webkit.messageHandlers.showMobile.postMessage(null)
      }

      function btnClick2(){
        window.webkit.messageHandlers.showName.postMessage('xiao黃')
      }

      function btnClick3(){
        window.webkit.messageHandlers.showSendMsg.postMessage(['13300001111', 'Go Climbing This Weekend !!!'])
      }

    </script>

</head>

<body>
    <br/>
    <div>
        <label>自己寫html</label>
    </div>
    <br/>
    <div id="mobile"></div>
    <div class="div">
        <button class="btn" type="button" onclick="btnClick1()">小紅帽的手機號</button>
    </div>
    <br/>
    <div id="name"></div>
    <div class="div">
        <button class="btn" type="button" onclick="btnClick2()">打電話給小紅帽</button>
    </div>
    <br/>
    <div id="msg"></div>
    <div class="div">
        <button class="btn" type="button" onclick="btnClick3()">發(fā)短信給小紅帽</button>
    </div>
    <br/>

</body>

</html>

關于html的內(nèi)容痴腌,我在這里不多加解釋,有興趣的同學可以去學習一下關于h5燃领,css士聪,javascript的相關知識。

WKWebView不支持nib文件猛蔽,所以這里需要使用代碼初始化并加載WebView

/*設置configur對象的WKUserContentController屬性的信息剥悟,也就是設置js可與webview內(nèi)容交互配置
     1、通過這個對象可以注入js名稱曼库,在js端通過window.webkit.messageHandlers.自定義的js名稱.postMessage(如果有參數(shù)可以傳遞參數(shù))方法來發(fā)送消息到native区岗;
     2、我們需要遵守WKScriptMessageHandler協(xié)議毁枯,設置代理,然后實現(xiàn)對應代理方法(userContentController:didReceiveScriptMessage:);
     3慈缔、在上述代理方法里面就可以拿到對應的參數(shù)以及原生的方法名,我們就可以通過NSSelectorFromString包裝成一個SEL种玛,然后performSelector調(diào)用就可以了
     4藐鹤、以上內(nèi)容是WKWebview和UIWebview針對JS調(diào)用原生的方法最大的區(qū)別(UIWebview中主要是通過是否允許加載對應url的那個代理方法,通過在js代碼里面寫好特殊的url赂韵,然后攔截到對應的url娱节,進行字符串的匹配以及截取操作,最后包裝成SEL祭示,然后調(diào)用就可以了)
     */
    
    /*
     上述是理論說明肄满,結(jié)合下面的實際代碼再做一次解釋,保你一看就明白
     1质涛、通過addScriptMessageHandler:name:方法悄窃,我們就可以注入js名稱了,其實這個名稱最好就是跟你的方法名一樣,這樣方便你包裝使用蹂窖,我這里自己寫的就是openBigPicture轧抗,對應js中的代碼就是window.webkit.messageHandlers.openBigPicture.postMessage()
     2、因為我的方法是有參數(shù)的瞬测,參數(shù)就是圖片的url横媚,因為點擊網(wǎng)頁中的圖片纠炮,要調(diào)用原生的瀏覽大圖的方法,所以你可以通過字符串拼接的方式給"openBigPicture"拼接成"openBigPicture:"灯蝴,我這里沒有采用這種方式恢口,我傳遞的參數(shù)直接是字典,字典里面放了方法名以及圖片的url穷躁,到時候直接取出來用就可以了
     3耕肩、我的js代碼中關于這塊的代碼是
     window.webkit.messageHandlers.openBigPicture.postMessage({methodName:"openBigPicture:",imageSrc:imageArray[this.index].src});
     4、js和原生交互這塊內(nèi)容離不開
     - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{}這個代理方法问潭,這個方法以及參數(shù)說明請到下面方法對應處
    
    */


WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
    //設置configur對象的preferences屬性的信息
    config.preferences.minimumFontSize = 18;
    
    WKWebView *wkView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, 414, 735/2) configuration:config];
    wkView.navigationDelegate=self;
    [self.view addSubview:wkView];
    self.wkwebView = wkView;
    
    
    NSString *filePath=[[NSBundle mainBundle] pathForResource:@"myindex" ofType:@"html"];
    NSURL *baseUrl=[[NSBundle mainBundle] bundleURL];
    [self.wkwebView loadHTMLString:[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil] baseURL:baseUrl];
    
    WKUserContentController *userController=config.userContentController;
    
    //JS調(diào)用OC 添加處理腳本
    [userController addScriptMessageHandler:self name:@"showMobile"];
    [userController addScriptMessageHandler:self name:@"showName"];
    [userController addScriptMessageHandler:self name:@"showSendMsg"];

在代理方法中處理相關的操作猿诸,js調(diào)用oc的代碼

//js調(diào)用oc,通過這個代理方法進行攔截
/*
 1狡忙、js調(diào)用原生的方法就會走這個方法
 2梳虽、message參數(shù)里面有2個參數(shù)我們比較有用,name和body灾茁,
 2.1 :其中name就是之前已經(jīng)通過addScriptMessageHandler:name:方法注入的js名稱
 2.2 :其中body就是我們傳遞的參數(shù)了窜觉,比如說我在js端傳入的是一個字典,所以取出來也是字典
 */

-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    if ([message.name isEqualToString:@"showMobile"]) {
        [self alertMessage:@"這是下面的小紅帽 手機號 123333333"];
    }
    if ([message.name isEqualToString:@"showName"]) {
        NSString *info=[NSString stringWithFormat:@"%@",message.body];
        [self alertMessage:info];
    }
    if ([message.name isEqualToString:@"showSendMsg"]) {
        NSArray *arr=message.body;
        NSString *info=[NSString stringWithFormat:@"%@%@",arr.firstObject,arr.lastObject];
        [self alertMessage:info];
    }
}
-(void)alertMessage:(NSString *)msg{
    
    UIAlertController *alertController=[UIAlertController alertControllerWithTitle:@"信息" message:msg preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *ok=[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
    }];
    
    [alertController addAction:ok];
    [self presentViewController:alertController animated:YES completion:^{
        
    }];
    
}

下面的是oc調(diào)用js北专,分開寫主要是為了讓大家看清楚每部分的代碼禀挫,已經(jīng)相關的解釋

- (IBAction)clearBtn:(id)sender {
    [self.wkwebView evaluateJavaScript:@"clear()" completionHandler:nil];
}
//oc調(diào)用js,通過evaluateJavaScript:注入方法名
- (IBAction)clickBtnItem:(UIButton *)sender {
    switch (sender.tag) {
        case 100:
        {
            [self.wkwebView evaluateJavaScript:@"alertMobile()" completionHandler:nil];
        }
            break;
            
        case 101:
        {
            [self.wkwebView evaluateJavaScript:@"alertName('小紅毛')" completionHandler:nil];
        }
            break;
            
        case 102:
        {
            [self.wkwebView evaluateJavaScript:@"alertSendMsg('18870707070','周末爬山真是件愉快的事情')" completionHandler:nil];
        }
            break;
            
        default:
            break;
    }
}

這是我拿出html里面javaScript的代碼拓颓,供大家閱讀语婴,里面我都有標注的注釋。

<script>
        function clear(){
            document.getElementById('mobile').innerHTML=''
            document.getElementById('name').innerHTML=''
            document.getElementById('msg').innerHTML=''
        }
       //oc調(diào)用js的方法列表
       function alertMobile(){
               //這里已經(jīng)調(diào)用過來了 但是搞不明白為什么alert方法沒有響應
                //alert('我是上面的小黃 手機號是:13300001111')
             document.getElementById('mobile').innerHTML='我是上面的小黃 手機號是:13300001111'
       }

      function alertName(msg){
             document.getElementById('name').innerHTML='你好 ' + msg + ', 我也很高興見到你'
      }
      
      function alertSendMsg(num,msg){
        document.getElementById('msg').innerHTML='這是我的手機號:' + num + ',' + msg + '!!'
      }

      //JS響應方法列表
      function btnClick1(){
        window.webkit.messageHandlers.showMobile.postMessage(null)
        //window.webkit.messageHandlers.showMobile.postMessage(null)
      }

      function btnClick2(){
        window.webkit.messageHandlers.showName.postMessage('xiao黃')
      }

      function btnClick3(){
        window.webkit.messageHandlers.showSendMsg.postMessage(['13300001111', 'Go Climbing This Weekend !!!'])
      }

    </script>

四录粱、學習總結(jié)

到此,關于js和原生的交互系列的示例已完成画拾,過程中遇到很多坑啥繁,但也很有收獲,關于我的描述不清青抛,或者不妥的地方旗闽,請大家指出。每篇文章都會對知識點進行總結(jié)蜜另,在文章末尾給出相關鏈接和示例DEMO的地址适室,同樣本文的示例也已放在GitHub上,需要的同學取走不謝举瑰。關于這幾篇文章的DEMO捣辆,對比學習,在看的過程中有什么呢疑問此迅,歡迎在下面留言汽畴,若發(fā)現(xiàn)文章中有那些地方?jīng)]有闡述清旧巾,或者沒有提到也可以留言告訴我,我們公司最近一直是h5和原生的app想結(jié)合忍些,所以研究一段時間鲁猩。隨著H5的強大,hybrid app已經(jīng)成為當前互聯(lián)網(wǎng)的大方向罢坝,單純的native app和web app在某些方面顯得就很劣勢廓握。
  
戳這里:本文的DEMO地址歡迎star

參考資料(戳這里):

http://nshipster.cn/wkwebkit/
http://www.huangyibiao.com/archives/742

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嘁酿,隨后出現(xiàn)的幾起案子隙券,更是在濱河造成了極大的恐慌,老刑警劉巖痹仙,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件是尔,死亡現(xiàn)場離奇詭異,居然都是意外死亡开仰,警方通過查閱死者的電腦和手機拟枚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來众弓,“玉大人恩溅,你說我怎么就攤上這事∥酵蓿” “怎么了脚乡?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長滨达。 經(jīng)常有香客問我奶稠,道長,這世上最難降的妖魔是什么捡遍? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任锌订,我火速辦了婚禮,結(jié)果婚禮上画株,老公的妹妹穿的比我還像新娘辆飘。我一直安慰自己,他們只是感情好谓传,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布蜈项。 她就那樣靜靜地躺著,像睡著了一般续挟。 火紅的嫁衣襯著肌膚如雪紧卒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天诗祸,我揣著相機與錄音常侦,去河邊找鬼浇冰。 笑死,一個胖子當著我的面吹牛聋亡,可吹牛的內(nèi)容都是我干的肘习。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼坡倔,長吁一口氣:“原來是場噩夢啊……” “哼漂佩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起罪塔,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤投蝉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后征堪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘩缆,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年佃蚜,在試婚紗的時候發(fā)現(xiàn)自己被綠了庸娱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡谐算,死狀恐怖熟尉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情洲脂,我是刑警寧澤斤儿,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站恐锦,受9級特大地震影響往果,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜一铅,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一陕贮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧馅闽,春花似錦飘蚯、人聲如沸馍迄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽攀圈。三九已至暴凑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赘来,已是汗流浹背现喳。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工凯傲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嗦篱。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓冰单,卻偏偏與公主長得像,于是被迫代替她去往敵國和親灸促。 傳聞我的和親對象是個殘疾皇子诫欠,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

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