WKWebView與JS交互之實戰技巧介紹

前言

上一篇專門講解了WKWebView相關的所有類、代理的所有API。前篇文章地址:http://blog.csdn.net/baihuaxiu123/article/details/51286109

那么本篇講些什么呢?當然是實戰了!先看看效果圖.

效果圖

通過本篇文章,至少可以學習到:

OC如何給JS注入對象及JS如何給IOS發送數據

JS調用alert、confirm、prompt時,不采用JS原生提示,而是使用iOS原生來實現

如何監聽web內容加載進度、是否加載完成

如何處理去跨域問題

創建配置類

在創建WKWebView之前,需要先創建配置對象,用于做一些配置:

WKWebViewConfiguration *config=[[WKWebViewConfiguration alloc] init];

配置偏好設置

偏好設置也沒有必須去修改它,都使用默認的就可以了,除非你真的需要修改它:

// 設置偏好設置config.preferences= [[WKPreferences alloc] init];// 默認為0config.preferences.minimumFontSize=10;// 默認認為YESconfig.preferences.javaScriptEnabled= YES;// 在iOS上默認為NO,表示不能自動通過窗口打開config.preferences.javaScriptCanOpenWindowsAutomatically= NO;

配置web內容處理池

其實我們沒有必要去創建它,因為它根本沒有屬性和方法:

// web內容處理池,由于沒有屬性可以設置,也沒有方法可以調用,不用手動創建

config.processPool=[[WKProcessPool alloc] init];

配置Js與Web內容交互

WKUserContentController是用于給JS注入對象的,注入對象后,JS端就可以使用:

window.webkit.messageHandlers..postMessage()

來調用發送數據給iOS端,比如:

window.webkit.messageHandlers.AppModel.postMessage({body:'傳數據'});

AppModel就是我們要注入的名稱,注入以后,就可以在JS端調用了,傳數據統一通過body傳,可以是多種類型,只支持NSNumber, NSString, NSDate, NSArray,NSDictionary, and NSNull類型。

下面我們配置給JS的main frame注入AppModel名稱,對于JS端可就是對象了:

// 通過JS與webview內容交互config.userContentController = [[WKUserContentController alloc] init];// 注入JS對象名稱AppModel,當JS通過AppModel來調用時,// 我們可以在WKScriptMessageHandler代理中接收到[config.userContentController addScriptMessageHandler:selfname:@"AppModel"];

當JS通過AppModel發送數據到iOS端時,會在代理中收到:

#pragmamark - WKScriptMessageHandler- (void)userContentController:(WKUserContentController *)userContentController? ? ? didReceiveScriptMessage:(WKScriptMessage *)message {if([message.name isEqualToString:@"AppModel"]) {// 打印所傳過來的參數,只支持NSNumber, NSString, NSDate, NSArray,// NSDictionary, and NSNull類型NSLog(@"%@", message.body);? }}

所有JS調用iOS的部分,都只可以在此處使用哦。當然我們也可以注入多個名稱(JS對象),用于區分功能。

創建WKWebView

通過唯一的默認構造器來創建對象:

self.webView= [[WKWebView alloc] initWithFrame:self.view.boundsconfiguration:config];[self.viewaddSubview:self.webView];

加載H5頁面

NSURL *path =[[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"];

[self.webView loadRequest:[NSURLRequest requestWithURL:path]];

配置代理

如果需要處理web導航條上的代理處理,比如鏈接是否可以跳轉或者如何跳轉,需要設置代理;而如果需要與在JS調用alert、confirm、prompt函數時,通過JS原生來處理,而不是調用JS的alert、confirm、prompt函數,那么需要設置UIDelegate,在得到響應后可以將結果反饋到JS端:

// 導航代理self.webView.navigationDelegate =self;// 與webview UI交互代理self.webView.UIDelegate =self;

添加對WKWebView屬性的監聽

// 添加KVO監聽[self.webViewaddObserver:selfforKeyPath:@"loading"options:NSKeyValueObservingOptionNew? ? ? ? ? ? ? ? context:nil];[self.webViewaddObserver:selfforKeyPath:@"title"options:NSKeyValueObservingOptionNew? ? ? ? ? ? ? ? context:nil];[self.webViewaddObserver:selfforKeyPath:@"estimatedProgress"options:NSKeyValueObservingOptionNew? ? ? ? ? ? ? ? context:nil];

然后我們就可以實現KVO處理方法,在loading完成時,可以注入一些JS到web中。這里只是簡單地執行一段web中的JS函數:

- (void)observeValueForKeyPath:(NSString*)keyPath? ? ? ? ? ? ? ? ? ? ? ofObject:(id)object? ? ? ? ? ? ? ? ? ? ? ? change:(NSDictionary *)change? ? ? ? ? ? ? ? ? ? ? context:(void*)context {if([keyPath isEqualToString:@"loading"]) {NSLog(@"loading");? }elseif([keyPath isEqualToString:@"title"]) {self.title=self.webView.title;? }elseif([keyPath isEqualToString:@"estimatedProgress"]) {NSLog(@"progress: %f",self.webView.estimatedProgress);self.progressView.progress=self.webView.estimatedProgress;? }// 加載完成if(!self.webView.loading) {// 手動調用JS代碼// 每次頁面完成都彈出來,大家可以在測試時再打開NSString*js = @"callJsAlert()";? ? [self.webViewevaluateJavaScript:js completionHandler:^(id_Nullable response,NSError* _Nullable error) {NSLog(@"response: %@ error: %@", response, error);NSLog(@"call js alert by native");? ? }];? ? [UIViewanimateWithDuration:0.5animations:^{self.progressView.alpha=0;? ? }];? }}

WKUIDelegate

與JS原生的alert、confirm、prompt交互,將彈出來的實際上是我們原生的窗口,而不是JS的。在得到數據后,由原生傳回到JS:

#pragma mark - WKUIDelegate- (void)webViewDidClose:(WKWebView *)webView {NSLog(@"%s", __FUNCTION__);}// 在JS端調用alert函數時,會觸發此代理方法。// JS端調用alert時所傳的數據可以通過message拿到// 在原生得到結果后,需要回調JS,是通過completionHandler回調- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString*)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void(^)(void))completionHandler {NSLog(@"%s", __FUNCTION__);? UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"alert"message:@"JS調用alert"preferredStyle:UIAlertControllerStyleAlert];? [alert addAction:[UIAlertAction actionWithTitle:@"確定"style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {? ? completionHandler();? }]];? [selfpresentViewController:alert animated:YEScompletion:NULL];NSLog(@"%@", message);}// JS端調用confirm函數時,會觸發此方法// 通過message可以拿到JS端所傳的數據// 在iOS端顯示原生alert得到YES/NO后// 通過completionHandler回調給JS端- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString*)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void(^)(BOOLresult))completionHandler {NSLog(@"%s", __FUNCTION__);? UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"confirm"message:@"JS調用confirm"preferredStyle:UIAlertControllerStyleAlert];? [alert addAction:[UIAlertAction actionWithTitle:@"確定"style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {? ? completionHandler(YES);? }]];? [alert addAction:[UIAlertAction actionWithTitle:@"取消"style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {? ? completionHandler(NO);? }]];? [selfpresentViewController:alert animated:YEScompletion:NULL];NSLog(@"%@", message);}// JS端調用prompt函數時,會觸發此方法// 要求輸入一段文本// 在原生輸入得到文本內容后,通過completionHandler回調給JS- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt defaultText:(nullableNSString*)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void(^)(NSString* __nullable result))completionHandler {NSLog(@"%s", __FUNCTION__);NSLog(@"%@", prompt);? UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"textinput"message:@"JS調用輸入框"preferredStyle:UIAlertControllerStyleAlert];? [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {? ? textField.textColor= [UIColorredColor];? }];? [alert addAction:[UIAlertAction actionWithTitle:@"確定"style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {? ? completionHandler([[alert.textFieldslastObject] text]);? }]];? [selfpresentViewController:alert animated:YEScompletion:NULL];}

WKNavigationDelegate

如果需要處理web導航操作,比如鏈接跳轉、接收響應、在導航開始、成功、失敗等時要做些處理,就可以通過實現相關的代理方法:

#pragma mark - WKNavigationDelegate// 請求開始前,會先調用此代理方法// 與UIWebView的// - (BOOL)webView:(UIWebView *)webView// shouldStartLoadWithRequest:(NSURLRequest *)request// navigationType:(UIWebViewNavigationType)navigationType;// 類型,在請求先判斷能不能跳轉(請求)- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void(^)(WKNavigationActionPolicy))decisionHandler {NSString*hostname = navigationAction.request.URL.host.lowercaseString;if(navigationAction.navigationType== WKNavigationTypeLinkActivated? ? ? && ![hostname containsString:@".baidu.com"]) {// 對于跨域,需要手動跳轉[[UIApplicationsharedApplication] openURL:navigationAction.request.URL];// 不允許web內跳轉decisionHandler(WKNavigationActionPolicyCancel);? }else{self.progressView.alpha=1.0;? ? decisionHandler(WKNavigationActionPolicyAllow);? }NSLog(@"%s", __FUNCTION__);}// 在響應完成時,會回調此方法// 如果設置為不允許響應,web內容就不會傳過來- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void(^)(WKNavigationResponsePolicy))decisionHandler {? decisionHandler(WKNavigationResponsePolicyAllow);NSLog(@"%s", __FUNCTION__);}// 開始導航跳轉時會回調- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {NSLog(@"%s", __FUNCTION__);}// 接收到重定向時會回調- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {NSLog(@"%s", __FUNCTION__);}// 導航失敗時會回調- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError*)error {NSLog(@"%s", __FUNCTION__);}// 頁面內容到達main frame時回調- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {NSLog(@"%s", __FUNCTION__);}// 導航完成時,會回調(也就是頁面載入完成了)- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {NSLog(@"%s", __FUNCTION__);}// 導航失敗時會回調- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError*)error {}// 對于HTTPS的都會觸發此代理,如果不要求驗證,傳默認就行// 如果需要證書驗證,與使用AFN進行HTTPS證書驗證是一樣的- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void(^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler {NSLog(@"%s", __FUNCTION__);? completionHandler(NSURLSessionAuthChallengePerformDefaultHandling,nil);}// 9.0才能使用,web內容處理中斷時會觸發- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {NSLog(@"%s", __FUNCTION__);}

JS端代碼

iOS and Js*{font-size:40px;}

Test how to use objective-c call js

Click me here:Jump to Baidu


functioncallJsAlert(){alert('Objective-C call js to show alert');? ? ? ? window.webkit.messageHandlers.AppModel.postMessage({body:'call js alert in js'});? ? ? }functioncallJsConfirm(){if(confirm('confirm','Objective-C call js to show confirm')) {? ? ? ? document.getElementById('jsParamFuncSpan').innerHTML? ? ? ? ='true';? ? ? }else{? ? ? ? document.getElementById('jsParamFuncSpan').innerHTML? ? ? ? ='false';? ? ? }// AppModel是我們所注入的對象window.webkit.messageHandlers.AppModel.postMessage({body:'call js confirm in js'});? ? }functioncallJsInput(){varresponse = prompt('Hello','Please input your name:');? ? ? document.getElementById('jsParamFuncSpan').innerHTML = response;// AppModel是我們所注入的對象window.webkit.messageHandlers.AppModel.postMessage({body: response});? ? }


備注:備注:內容來自CSDN-劉玉剛--AI-技術研究院,原文博客網址:http://blog.csdn.net/baihuaxiu123/article/details/51287367

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容