WKWebView 在與 JS 的交互時特有的方法:
WKUIDelegate 方法
MessageHandler 方法
- WKUIDelegate 方法
WKUIDelegate 協(xié)議包含一些函數(shù)用來監(jiān)聽 web JS 想要顯示 alert 或 confirm 時觸發(fā)。我們?nèi)绻?WKWebView 中加載一個 web 并且想要 web JS 的 alert 或 confirm 正常彈出,就需要實現(xiàn)對應(yīng)的代理方法。( 如果沒有實現(xiàn)對應(yīng)的代理方法,則 webview 將會按照默認操作去做出行為。)
下面是在 WKUIDelegate 監(jiān)聽 web 要顯示 alert 的代理方法中用 Native UIAlertController 替代 JS 中的 alert 顯示的例子 :
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
// 用 Native 的 UIAlertController 彈窗顯示 JS 將要提示的信息
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提醒" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
// 函數(shù)內(nèi)必須調(diào)用 completionHandler
completionHandler();
}]];
[self presentViewController:alert animated:YES completion:nil];
}
- MessageHandler 方法
MessageHandler 是繼 Native 截獲 JS 假請求后另一種 JS 調(diào)用 Native 的方法,該方法利用了 WKWebView 的新特性實現(xiàn)。對比截獲假 Request 的方法來說,MessageHandler 傳參數(shù)相對簡單方便。
MessageHandler 指什么?
WKUserContentController 類有一個方法:
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
該方法用來添加一個腳本處理器,可以在處理器內(nèi)對 JS 腳本調(diào)用的方法做出處理,從而達到 JS 調(diào)用 Native 的目的。
那么 WKUserContentController 類和 WKWebView 有什么關(guān)系呢?
在 WKWebView 的初始化函數(shù)中有一個入?yún)?configuration,它的類型是 WKWebViewConfiguration。WKWebViewConfiguration 中包含一個屬性 userContentController,這個 userContentController 就是 WKUserContentController 類型的實例,我們可以用這個 userContentController 來添加不同名稱的腳本處理器。
2.1. MessageHandler 的坑
回到 - (void)addScriptMessageHandler:name: 方法上面,該方法添加一個腳本消息處理器(第一個入?yún)?scriptMessageHandler),并且給這個處理器起一個名字(第二個入?yún)?name)。不過這個函數(shù)在使用的時候有個坑:scriptMessageHandler 入?yún)粡娨?,那么如果你把當?WKWebView 所在的 UIViewController 作為第一個入?yún)?,這個 viewController 被他自己所持有的 webview.configuration. userContentController 所持有,就會造成循環(huán)引用。

我們可以通過 - (void)removeScriptMessageHandlerForName: 方法刪掉 userContentController 對 viewController 的強引用。所以一般情況下我們的代碼會在 viewWillAppear 和 viewWillDisappear 成對兒的添加和刪除 MessageHandler:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.webview.configuration.userContentController addScriptMessageHandler:self name:@"YourFuncName"];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.webview.configuration.userContentController removeScriptMessageHandlerForName:@"YourFuncName"];
}
WKScriptMessageHandler 協(xié)議
WKScriptMessageHandler 是腳本信息處理器協(xié)議,如果想讓一個對象具有腳本信息處理能力就必須使其遵循該協(xié)議。
WKScriptMessageHandler 協(xié)議內(nèi)部非常簡單,只有一個方法,我們必須要實現(xiàn)該方法(@required):
// WKScriptMessageHandler 協(xié)議方法,在接收到腳本信息時觸發(fā)
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
// message 有兩個屬性:name 和 body
// message.name 可以用于區(qū)別要做的處理
if ([message.name isEqualToString:@"YourFuncName"]) {
// message.body 相當于 JS 傳遞過來的參數(shù)
NSLog(@"JS call native success %@", message.body);
}
}
補充 JS 的代碼:
// <name> 換 YourFuncName,<messageBody> 換你要的入?yún)⒓纯?window.webkit.messageHandlers.<name>.postMessage(<messageBody>)