使用WKWebView的時候,如果想要實現(xiàn)JS調(diào)用OC方法,除了攔截URL之外,還有一種簡單的方式。那就是利用WKWebView的新特性MessageHandler來實現(xiàn)JS調(diào)用原生方法。
MessageHandler 是什么?
WKWebView 初始化時,有一個參數(shù)叫configuration,它是WKWebViewConfiguration類型的參數(shù),而WKWebViewConfiguration有一個屬性叫userContentController,它又是WKUserContentController類型的參數(shù)。WKUserContentController對象有一個方法- addScriptMessageHandler:name:,我把這個功能簡稱為MessageHandler。
- addScriptMessageHandler:name:有兩個參數(shù),第一個參數(shù)是userContentController的代理對象,第二個參數(shù)是JS里發(fā)送postMessage的對象。
所以要使用MessageHandler功能,就必須要實現(xiàn)WKScriptMessageHandler協(xié)議。
我們在該API的描述里可以看到在JS中的使用方法:
window.webkit.messageHandlers.<name>.postMessage(<messageBody>) //其中<name>,就是上面方法里的第二個參數(shù)`name`。 //例如我們調(diào)用API的時候第二個參數(shù)填@"Share",那么在JS里就是: //window.webkit.messageHandlers.Share.postMessage(<messageBody>) //<messageBody>是一個鍵值對,鍵是body,值可以有多種類型的參數(shù)。 // 在`WKScriptMessageHandler`協(xié)議中,我們可以看到mssage是`WKScriptMessage`類型,有一個屬性叫body。 // 而注釋里寫明了body 的類型: Allowed types are NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull.
怎么使用MessageHandler?
1.創(chuàng)建WKWebViewConfiguration對象,配置各個API對應的MessageHandler。
WKUserContentController對象可以添加多個scriptMessageHandler。
// 這是創(chuàng)建configuration 的過程 WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; WKPreferences *preferences = [WKPreferences new]; preferences.javaScriptCanOpenWindowsAutomatically = YES; preferences.minimumFontSize = 40.0; configuration.preferences = preferences;
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.webView.UIDelegate = self; self.webView.navigationDelegate = self; NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"html"]; NSURL *url = [NSURL fileURLWithPath:path]; // [NSURL URLWithString:@"http://www.baidu.com"]; // NSURLRequest *request = [NSURLRequest requestWithURL:url]; [self.webView loadRequest:request]; // addScriptMessageHandler 很容易導致循環(huán)引用 // 控制器 強引用了WKWebView,WKWebView copy(強引用了)configuration, configuration copy (強引用了)userContentController // userContentController 強引用了 self (控制器) [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"clickYou"]; }
需要注意的是addScriptMessageHandler很容易引起循環(huán)引用,導致控制器無法被釋放,所以需要加入以下這段:
- (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // 因此這里要記得移除handlers [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"clickYou"]; }
2.實現(xiàn)協(xié)議方法。
我這里實現(xiàn)了兩個協(xié)議<WKUIDelegate,WKScriptMessageHandler>,WKUIDelegate是因為我在JS中彈出了alert 。WKScriptMessageHandler是因為我們要處理JS調(diào)用OC方法的請求。
先看實現(xiàn)協(xié)議方法的示例代碼:
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ NSLog(@"%@:%@",message.name,message.body); if([message.name isEqualToString:@"clickYou"]){ [self clickYou]; } }
WKScriptMessage有兩個關鍵屬性name 和 body。
因為我們給每一個OC 方法取了一個name,那么我們就可以根據(jù)name 來區(qū)分執(zhí)行不同的方法。body 中存著JS 要給OC 傳的參數(shù)。
3.處理HTML中JS調(diào)用。
function btnClick(){ //JS調(diào)用 window.webkit.messageHandlers.clickYou.postMessage({ "Acer": 500, "Dell": 600 }); alert(1); }
注意其中的clickYou,根據(jù)OC中注冊的name一樣.
4.OC調(diào)用JS
NSString *title = @"主題"; NSString *content = @"內(nèi)容"; NSString *url = @"http://www.baidu.com"; NSString *jsStr = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url]; [self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) { }];
function shareResult(title,content,url){ alert("title:"+title+"content:"+content+"url:"+url); }
5.實現(xiàn)WKUIDelegate
如果JS中需要alert,那么實現(xiàn)WKUIDelegate三個代理方法,如下:
// alert框 /** alert框 @param webView WKWebView @param message 消息文本內(nèi)容 @param frame frame主窗口 @param completionHandler 窗口消失回調(diào) */ - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{ kFunLog UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:([UIAlertAction actionWithTitle:@"確認" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler(); }])]; [self presentViewController:alertController animated:YES completion:nil]; } // 確認框 - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{ kFunLog UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { completionHandler(NO); }])]; [alertController addAction:([UIAlertAction actionWithTitle:@"確認" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler(YES); }])]; [self presentViewController:alertController animated:YES completion:nil]; } // 文本輸入框 - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler{ kFunLog UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert]; [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { textField.text = defaultText; }]; [alertController addAction:([UIAlertAction actionWithTitle:@"完成" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler(alertController.textFields[0].text?:@""); }])]; [self presentViewController:alertController animated:YES completion:nil]; } - (BOOL)webView:(WKWebView *)webView shouldPreviewElement:(WKPreviewElementInfo *)elementInfo API_AVAILABLE(ios(10.0)){ return YES; }
浙公網(wǎng)安備 33010602011771號