iOS WKWebView OC 與 JS 交互學習
我寫WKWebView 想讓 服務端相應 一個 方法但是不響應,根據 UIWebView 用 JSContext就能拿到響應的處理經驗是不是服務端 也需要 對 WKwebView有兼容的一個寫法??? 特此學習 WKWebView 記錄
一 .WKWebView 代理協議
(1)WKScriptMessageHandler :
OC在JS調用方法時做的處理。如果需要調用對話窗口就會先執行(3)協議再執行 (1)協議
好處:傳遞給OC的參數直接在字典里面,不用再在url里面拼湊后的結果去截取解析。
必須實現
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
(2)WKNavigationDelegate:
處理頁面跳轉和載入過程。
#pragma mark -- 進行頁面跳轉 // 接收到服務器跳轉請求之后再執行 - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation; // 在發送請求之前,決定是否跳轉 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler ; // 在收到響應后,決定是否跳轉 - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler ;
#pragma mark -- 追蹤加載過程 // 頁面開始加載時調用 - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation; // 當內容開始返回時調用 - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation; // 頁面加載完成之后調用 - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation; // 頁面加載失敗時調用 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error; //好多協議 按需添加。。。
(3)WKUIDelegate
這個協議主要用于WKWebView處理web界面的三種提示框(警告框、確認框、輸入框) 的網頁交互
#pragma mark - WKUIDelegate //警告 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(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 ,關鍵在當前頁面銷毀后要注銷協議保證無強引用,當前頁面也無法銷毀,發生內存泄漏。
//注冊協議 self.userContentController = [[WKUserContentController alloc] init]; //注冊方法 WKDelegateController *delegateController = [[WKDelegateController alloc]init]; delegateController.delegate = self; // 通過JS與webview內容交互 config.userContentController = self.userContentController; // 注入JS對象名稱HFAlert,HFConfirm,HFPrompt當JS通過HFAlert,HFConfirm,HFPrompt來調用時, // 我們可以在WKScriptMessageHandler代理中接收到 [config.userContentController addScriptMessageHandler:delegateController name:HFAlert]; [config.userContentController addScriptMessageHandler:delegateController name:HFConfirm]; [config.userContentController addScriptMessageHandler:delegateController name:HFPrompt]; [config.userContentController addScriptMessageHandler:delegateController name:HFShare]; //注銷協議 - (void)dealloc { [self.userContentController removeScriptMessageHandlerForName:HFAlert]; [self.userContentController removeScriptMessageHandlerForName:HFConfirm]; [self.userContentController removeScriptMessageHandlerForName:HFPrompt]; [self.userContentController removeScriptMessageHandlerForName:HFShare]; } //實施協議 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { NSLog(@"%@", message.body); if ([message.name isEqualToString:HFAlert]) { // 打印所傳過來的參數,只支持NSNumber, NSString, NSDate, NSArray, // NSDictionary, and NSNull類型 NSDictionary *dictionary = message.body; if (![dictionary isKindOfClass:[NSDictionary class]]) { return; } NSLog(@"%@",dictionary[@"body"]); //服務端傳值 log dispatch_async(dispatch_get_main_queue(), ^{ //主線程操作UI }); } else if ([message.name isEqualToString:HFConfirm]) { } else if ([message.name isEqualToString:HFPrompt]) { } else if ([message.name isEqualToString:HFShare]) { } }
設置代理 WKDelegateController 重寫成代理控制器性質,保證設置代理后,可移除 的關鍵!!!
// // WKDelegateController.m // SectionDemo // // Created by HF on 2017/6/22. // Copyright ? 2017年 HF-Liqun. All rights reserved. // #import "WKDelegateController.h" @interface WKDelegateController () @end @implementation WKDelegateController - (void)viewDidLoad { [super viewDidLoad]; } - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ if ([self.delegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]) { [self.delegate userContentController:userContentController didReceiveScriptMessage:message]; } } @end //////// // // WKDelegateController.h // SectionDemo // // Created by HF on 2017/6/22. // Copyright ? 2017年 HF-Liqun. All rights reserved. // #import <UIKit/UIKit.h> #import <WebKit/WebKit.h> @protocol WKDelegate <NSObject> - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message; @end @interface WKDelegateController : UIViewController <WKScriptMessageHandler> @property (weak , nonatomic) id < WKScriptMessageHandler > delegate; @end
(2)WKNavigationDelegate,舉例子和UIWebView shouldStart 方法一樣,在請求前判斷要不要繼續執行
// 在發送請求之前,決定是否跳轉 - (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"]) { // 對于跨域,需要手動跳轉 [[UIApplication sharedApplication] openURL:navigationAction.request.URL]; // 不允許web內跳轉 decisionHandler(WKNavigationActionPolicyCancel); } else { self.progressView.alpha = 1.0; decisionHandler(WKNavigationActionPolicyAllow); } NSLog(@"%s", __FUNCTION__); }
(3)WKUIDelegate 參考代碼
#pragma mark - WKUIDelegate 這個協議主要用于WKWebView處理web界面的三種提示框(警告框、確認框、輸入框) 的網頁交互/** * web界面中有彈出警告框時調用 * * @param webView 實現該代理的webview * @param message 警告框中的內容 * @param frame 主窗口 * @param completionHandler 警告框消失調用 無回調 */ - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler { NSLog(@"%s", __FUNCTION__); //completionHandler(@"空回調"); UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"alert" message:[NSString stringWithFormat:@"JS調用alert message:%@",message] preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler(); }]]; [self presentViewController:alert animated:YES completion:NULL]; NSLog(@"%@", message); } /** 確認框 @param webView 實現該代理的webview @param message 確認窗口內容 @param frame 主窗口 @param completionHandler 警告框消失調用 回傳布爾變量 */ - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler { NSLog(@"%s", __FUNCTION__); //completionHandler(@"可以直接回傳布爾變量"); 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); }]]; [self presentViewController:alert animated:YES completion:NULL]; NSLog(@"%@", message); } /** 輸入框 @param webView web @param prompt 文本 @param defaultText 默認輸入文本 @param frame frame @param completionHandler completionHandler 回傳 字符串 */ - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler { //completionHandler(@"可以直接回傳字符串"); //可以根據回傳參數來判斷 是否必要展示 彈框,也可以隱式處理 根據當前參數判斷回傳指定邏輯參數 NSLog(@"%s", __FUNCTION__); NSLog(@"%@", prompt); if ([prompt isEqualToString:HFPrompt]) { completionHandler(@"guess"); return; } UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"textinput" message:@"JS調用輸入框" preferredStyle:UIAlertControllerStyleAlert]; [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { textField.textColor = [UIColor redColor]; }]; [alert addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler([[alert.textFields lastObject] text]); }]]; [self presentViewController:alert animated:YES completion:NULL]; }
三.OC JS 互相調用
(1) OC 調用 JS 代碼(OC注入JS)
NSString *js = @"alertName('我是參數')";//可以向js傳參數
//NSString *js = @"callJsAlert()";//無參數方法 [self.webView evaluateJavaScript:js completionHandler:^(id _Nullable response, NSError * _Nullable error) { NSLog(@"response: %@ error: %@", response, error); NSLog(@"call js alert by native"); }];
(2) JS 調用 OC方法:關鍵是按照約定方法名和注冊名
關鍵代碼:
//name 為注冊名 HFAlert HFPrompt ... messageBody 為傳遞參數集合 和 UIWebView的 調用OC 方法 是不一樣的!!!!! window.webkit.messageHandlers.<name>.postMessage(<messageBody>);
參考示例:
<!DOCTYPE html> <html> <head> <title>iOS and Js</title> <style type="text/css"> * { font-size: 40px; } </style> </head> <body> <div style="margin-top: 100px"> <h1>Test how to use objective-c call js</h1><br/><br/> <div><input type="button" value="call shareButton" onclick="shareClick()"></div> <br/><br/> <div><input type="button" value="call js alert" onclick="callJsAlert()"></div> <br/> <div><input type="button" value="Call js confirm" onclick="callJsConfirm()"></div><br/> </div> <br/> <div> <div><input type="button" value="Call Js prompt " onclick="callJsInput()"></div><br/> <div>Click me here: <a href="http://www.baidu.com">Jump to Baidu</a></div> </div> <br/> <div id="SwiftDiv"> <span id="jsParamFuncSpan" style="color: red; font-size: 50px;"></span> </div> <script type="text/javascript"> //JS執行window.webkit.messageHandlers.Share.postMessage(<messageBody>) function callJsAlert() { //WKUIDelegate 空回調 警告框代理被觸發 alert('Objective-C call js to show alert'); // HFAlert是我們所注入的對象 window.webkit.messageHandlers.HFAlert.postMessage({body: 'call js alert in js'}); } function callJsConfirm() { //WKUIDelegate 布爾回調 選擇確認框代理被觸發 if (confirm('confirm', 'Objective-C call js to show confirm')) { document.getElementById('jsParamFuncSpan').innerHTML = 'true'; } else { document.getElementById('jsParamFuncSpan').innerHTML = 'false'; } //HFConfirm是我們所注入的對象 window.webkit.messageHandlers.HFConfirm.postMessage({body: 'call js confirm in js'}); } function callJsInput() { //WKUIDelegate 字符串回調 輸入框代理被觸發 var response = prompt('HFPrompt', 'Please input your name:'); document.getElementById('jsParamFuncSpan').innerHTML = response; //HFPrompt是我們所注入的對象 window.webkit.messageHandlers.HFPrompt.postMessage({body: response}); } function shareClick() { var response = prompt('Hello'); window.webkit.messageHandlers.HFShare.postMessage({body:response ,title:'測試分享的標題',content:'測試分享的內容',url:'https://github.com/maying1992'}); } function alertName(msg) { document.getElementById('jsParamFuncSpan').innerHTML = 'name' + msg + ', nice to meet you'; } </script> </body> </html>
end
參考
(1) http://www.tuicool.com/articles/qQRrMzY
(2)http://www.cocoachina.com/ios/20160906/17487.html
(3)http://www.jianshu.com/p/4fa8c4eb1316
(4)http://blog.csdn.net/u011619283/article/details/52135988
posted on 2017-06-22 18:02 ACM_Someone like you 閱讀(6536) 評論(0) 收藏 舉報
浙公網安備 33010602011771號