1. 程式人生 > >關於wkwebview與html5互動

關於wkwebview與html5互動

       之前寫過web與html5的互動,那時的載體是UIWebview,基於uiwebview屬於比較老舊的網頁載體,且記憶體消耗比較大,故今日打算把UIWebview替換成WKwebview,起初只認為是換個類名那麼簡單,替換過程才發現,是自己想的太簡單了,下面來說說uiweb與wkweb與html5互動時的異同(注:這篇文章只是針對原生與html5互動,且UIWebview與原生互動用的是javascriptcore框架):

一,關於代理方法與傳值

uiwebview常用的是UIWebviewDelegate 與JSExport,wkwebview常用的是WKSCriptMessageHandler,WKNavigationDelegate,WKUIDelagete。

 1, UIWebview的UIWebviewDelegate與WKWebview的WKNavigationDelegate功能相似,體現網頁的載入週期;這兩個協議裡的都有網頁在不同階段裡的代理方法,現列出如下:

   1》頁面準備載入:UIWebViewDelegate - webView:shouldStartLoadWithRequest:navigationType相當於 WKNavigationDelegate - webView:didStartProvisionalNavigation:

   2》網頁已開始載入:UIWebViewDelegate - webViewDidStartLoad: 相當於WKNavigationDelegate - webView:didCommitNavigation:

   3》頁面全部載入:WKNavigationDelegate - webView:didCommitNavigation:相當於WKNavigationDelegate - webView:didFinishNavigation 

   4》頁面載入失敗:UIWebViewDelegate - webView:didFailLoadWithError:相當於WKNavigationDelegate - webView:didFailNavigation:withError:或者WKNavigationDelegate - webView:didFailProvisionalNavigation:withError:

如果你之前只是用到了以上列出的 UIWebViewDelegate 中的幾個方法,那麼只是簡單地換一個方法名,讓你的 ViewController 繼承 WKNavigationDelegate ,繼續用就可以了。

2,UIWebView的JSExport與WKWebview的WKSCriptMessageHandler都是與js互動的;

   UIWebview與js互動分為js向oc傳資料與oc向js傳資料。而這雙向的傳值方法,都是宣告在遵守JSExport的在自定義協議中,當然這個自定義協議中的代理方法也都是自定義的。而WKwebview的WKSCriptMessageHandler協議中只有一個系統的方法,這個代理方法只能用來接收js傳到原生的資料,而原生要想給js傳資料,需要用WKWebViewConfiguration這個類物件的userContentController屬性(此屬性是WKUserContentController型別的),舉個��:

1》UIWebview

     oc給js傳資料:(1)oc端:在遵守JSExport的自定義協議中宣告一個方法-(void)getJsonText;

                              (2)oc端:在web的控制器的.m檔案實現該方法,實現此方法時,通過jsContex建立了JSValue型別的物件,此物件即為js中的一個全域性的方法

                                       -(void)getJsonText{

JSValue *getJsonTextJsMethod =self.jsContext[@"getJsonTextJsMethod"];

NSArray *array =@[@“你好嗎”];

                                                  NSString *string = [[NSStringalloc]initWithFormat:@"%@",array[0]];

                                                  [getJsonTextJsMethodcallWithArguments:array];

}

                           (3)js端:在js裡呼叫getJsonText方法,並在getJsonTextJsMethod中接收oc端來的資料                                                            

                                     $(document).ready(function() { jsInterface.getJsonText()}   //呼叫自定義協議的代理方法

                                    var getJsonTextJsMethod =function(json){}   // json即為從oc端來的資料

    js給oc傳資料:(1)oc端:在遵守JSExport的自定義協議中宣告一個方法-(void)gotoNext:(NSString*)string;

                            (2)oc端:在web的控制器的.m檔案實現該方法

                                           -(void)gotoNext:(NSString*)string{NSLog(@"gotoNex:%lu",string);}  //引數string即為從js傳到oc端的資料

                            (3)js端:在適當的地方給自定義代理方法的引數賦值即可,此引數即為要傳輸的資料;  

                                           jsInterface.gotoNext(‘很好啊’);

2》WKwebView

  wkwebview與js互動時,建立web的方式與uiwebview有所區別,建立方式如下:              

                                    WKWebViewConfiguration *config = [[WKWebViewConfigurationalloc] init];

self.webView = [[WKWebViewalloc] initWithFrame:[UIScreenmainScreen].boundsconfiguration:config];

                                    並設定兩個代理  self.webView.navigationDelegate =self;  self.webView.UIDelegate =self;

      oc給js傳資料:(1) oc端:WKWebview的 evaluateJavaScript方法,呼叫js的全域性方法,如下

                                [self.webViewevaluateJavaScript:@"jsMethodTwo('你好嗎')"completionHandler:nil]   //jsMethod為js的全域性方法,此方法的引數即為要傳的資料

                               (2)js端:在js的全域性方法中獲取傳來的資料     

                                function jsMethodTwo(msg) {parseJson(msg);}   // msg即為傳過來的資料,內容為'你好嗎'

      js給oc傳資料:(1)oc端:建立WKUserContentController類例項,並將passFromJsToOC新增到類此例項物件中,如下

                                                           WKWebViewConfiguration *config = [[WKWebViewConfigurationalloc] init];

WKUserContentController *userCC = config.userContentController

                                                           [userCCaddScriptMessageHandler:selfname:@"passFromJsToOC"]

                               (2)js端:呼叫如下方法:window.webkit.messageHandlers."oc中新增到WKUserContentController的方法名".postMessage('要傳的資料'),距離如下

                                window.webkit.messageHandlers.passFromJsToOC.postMessage('文字')  //passFromJsToOC為新增到WKUserContentController類的例項物件

                               (3)js端:在wkwebview的WKScriptMessageHandler協議的代理方法-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{}中,接收js傳過來的資料,如下:

                                     -(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{self.myString = message.body;}  //注 message.body即為傳過來的資料

二,關於彈框

1》UIWebview:

 h5程式碼與原生oc互動時,除錯是個很令人頭疼的問題,h5的程式碼要拷貝到我們本地的檔案中,h5的邏輯層js在我們的工程中打斷點不好使,除錯起來很受罪啊!還好js端有alert,這個alert彈框在UIWebview互動時還是挺給力的;

2》WKWebview:

上邊提到的alert是原生與h5互動的利器,可是在WKWebview時卻不好使了,在此我們就不得不提那個叫做WKUIDelegate的協議了,我們需要實現此協議的代理方法,此協議關於彈窗的代理方法有三個,分別針對js端不同的彈框,當然與我們除錯相關性最大的就是提示框alert了,其他兩個彈框分別是確認框confirm以及輸入框prompt;這三種彈框分別對應著三個不同的代理方法,代理方法裡要實現的東西就感覺比較噁心了,在代理方法裡我們需要建立oc原生的對應的彈窗,真不知道wkwebview為何要這麼弄一下。js彈框與之對應的代理方法及代理方法內的實現如下:

 2.1》js的提示框alert  對應的代理方法及其實現是:

-(void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{

UIAlertController *alertController = [UIAlertControlleralertControllerWithTitle:@"提示"message:message?:@""preferredStyle:UIAlertControllerStyleAlert];

    [alertController addAction:([UIAlertActionactionWithTitle:@"確認"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *_Nonnull action) {

        completionHandler();

    }])];

    [selfpresentViewController:alertControlleranimated:YEScompletion:nil];

}

 2.2》js的確認框confirm  對應的代理方法及其實現如下:

- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {

UIAlertController *alert = [UIAlertControlleralertControllerWithTitle:@"確認框"message:message preferredStyle:UIAlertControllerStyleAlert];

    [alert addAction:[UIAlertActionactionWithTitle:@"確定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *_Nonnull action) {

        completionHandler(YES);

    }]];

    [alert addAction:[UIAlertActionactionWithTitle:@"取消"style:UIAlertActionStyleCancelhandler:^(UIAlertAction *_Nonnull action) {

        completionHandler(NO);

    }]];

    [selfpresentViewController:alert animated:YEScompletion:NULL];

}

2.3》js的輸入框 對應的代理方法及實現如下:

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {

UIAlertController *alert = [UIAlertControlleralertControllerWithTitle:@"輸入框"message:prompt preferredStyle:UIAlertControllerStyleAlert];

    [alert addTextFieldWithConfigurationHandler:^(UITextField *_Nonnull textField) {

        textField.textColor = [UIColorblackColor];

        textField.placeholder = defaultText;

    }];

    [alert addAction:[UIAlertActionactionWithTitle:@"確定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *_Nonnull action) {

        completionHandler([[alert.textFieldslastObject] text]);

    }]];

    [alert addAction:[UIAlertActionactionWithTitle:@"取消"style:UIAlertActionStyleCancelhandler:^(UIAlertAction *_Nonnull action) {

        completionHandler(nil);

    }]];

    [selfpresentViewController:alert animated:YEScompletion:NULL];

}

當然了,如果不使用這個代理也是可以的,你可以先將你想要彈出的內容傳到原生oc端,在收到js傳來訊息的代理方法裡彈彈框,當然也是原生的彈框。

三、關於html5匯入本地的路徑

這個與web的載體無關,無論UIWebview與WKWebview都有這個問題

html5放在本地互動時,如果將html5檔案整個的按group的形式匯入,在載入本地html5時,js與css都沒有出現,後閉門造車的發現把html檔案中引入的js與css的路徑去掉之後,ok,問題解決了,於是乎,以為萬事ok了,最近在搗鼓WKWebview的時候,發現其實有其他更好的方法去解決這個問題。只需要在匯入html5檔案時換個方法就可以了,匯入過程如下:

1》 以group的方式匯入父資料夾


2》以folder的方式匯入子資料夾,即css js html資料夾


3》匯入結果截圖如下:


按上述方式到檔案後,執行正常,ok問題解決

四、關於oc頂用js的方法問題(只針對於WKWebview evaluateJavaScript):

上邊提到WKWebview的oc呼叫js的方法用的是evaluateJavaScript,呼叫js的方法分以下情況:

1》呼叫js無引數的方法(對於互動似乎沒啥意義):

oc端 [self.wkWebViewevaluateJavaScript:@"alertMobile()"completionHandler:nil]

js端 function alertMobile() {}

2》呼叫js有引數的方法(用引數傳資料)

[self.wkWebViewevaluateJavaScript:@"alertName('小紅')"completionHandler:nil]

function alertName(msg) {alert( msg)

3》呼叫js有引數的方法(用引數傳資料,用返回值對oc端做回執)

[self.wkWebViewevaluateJavaScript:@"alertSendMsg('哈嘍')"completionHandler:^(id result,NSError * _Nullable error) {NSLog(@"返回的值是:%@",result);}]

function alertSendMsg(num,msg) { document.getElementById('msg').innerHTML ='這是我的手機號:' + num +',' + msg + '!!'return'nihaoma';}

result即為js方法返回的nihaoma

感覺這個回執挺有用的,省去alert查看了

五、關於載入本地html並操控本地html跳頁

這個題目五有點煩人啊,乍看一臉茫然吧,是的,這種跳轉方式我也感覺一臉愕然。是的,就是這樣設計的,客戶端載入本地a頁面之後如果想跳的本地b網頁,是由客戶端操控的,並不是我們平時那樣的非本地的連結方式或本地的href方式,而是超出於宇宙之上的第三種方式--客戶端控制,並給下一頁傳報文。

1》UIWebview:

 需要前面提到的自定義協議裡的代理方法了,跳轉下一頁事件中,js傳值給oc,在oc獲取值時跳轉,然後按給js傳值的方式傳報文給js,當然這需要在網頁初始載入的時候傳過去

2》WKWebview:

這個就需要研究下web的載入週期了,依舊是在跳轉下一頁事件中,js傳值給oc,在oc獲取值時按載入前頁的方式載入第二頁,這是後報文就得跟上節奏適時傳給js了,這個傳報文的位置沒有UIwebview那麼自動了,我們需要在第一頁頁面載入完成的時候(即如下代理方法)把第二頁需要的報文按上述相應傳值方式傳過去。

-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{}

五、關於oc給js傳資料的資料格式

這裡只說WKWebview,UIWebview沒有這個問題

WKWebview中oc在給js傳值時,傳的資料不能有\n,否則傳不過去,如果你要穿的是報文之類的資料,千萬要遍歷一遍這個資料去掉裡邊的換行,否則是傳不過來的。