WKWebView 在与 JS 的交互时特有的方法:
WKUIDelegate 方法
MessageHandler 方法
- WKUIDelegate 方法
WKUIDelegate 协议包含一些函数用来监听 web JS 想要显示 alert 或 confirm 时触发。我们如果在 WKWebView 中加载一个 web 并且想要 web JS 的 alert 或 confirm 正常弹出,就需要实现对应的代理方法。( 如果没有实现对应的代理方法,则 webview 将会按照默认操作去做出行为。)
下面是在 WKUIDelegate 监听 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) {
// 函数内必须调用 completionHandler
completionHandler();
}]];
[self presentViewController:alert animated:YES completion:nil];
}
- MessageHandler 方法
MessageHandler 是继 Native 截获 JS 假请求后另一种 JS 调用 Native 的方法,该方法利用了 WKWebView 的新特性实现。对比截获假 Request 的方法来说,MessageHandler 传参数相对简单方便。
MessageHandler 指什么?
WKUserContentController 类有一个方法:
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
该方法用来添加一个脚本处理器,可以在处理器内对 JS 脚本调用的方法做出处理,从而达到 JS 调用 Native 的目的。
那么 WKUserContentController 类和 WKWebView 有什么关系呢?
在 WKWebView 的初始化函数中有一个入参 configuration,它的类型是 WKWebViewConfiguration。WKWebViewConfiguration 中包含一个属性 userContentController,这个 userContentController 就是 WKUserContentController 类型的实例,我们可以用这个 userContentController 来添加不同名称的脚本处理器。
2.1. MessageHandler 的坑
回到 - (void)addScriptMessageHandler:name: 方法上面,该方法添加一个脚本消息处理器(第一个入参 scriptMessageHandler),并且给这个处理器起一个名字(第二个入参 name)。不过这个函数在使用的时候有个坑:scriptMessageHandler 入参会被强引用,那么如果你把当前 WKWebView 所在的 UIViewController 作为第一个入参,这个 viewController 被他自己所持有的 webview.configuration. userContentController 所持有,就会造成循环引用。
我们可以通过 - (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 协议
WKScriptMessageHandler 是脚本信息处理器协议,如果想让一个对象具有脚本信息处理能力就必须使其遵循该协议。
WKScriptMessageHandler 协议内部非常简单,只有一个方法,我们必须要实现该方法(@required):
// WKScriptMessageHandler 协议方法,在接收到脚本信息时触发
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
// message 有两个属性:name 和 body
// message.name 可以用于区别要做的处理
if ([message.name isEqualToString:@"YourFuncName"]) {
// message.body 相当于 JS 传递过来的参数
NSLog(@"JS call native success %@", message.body);
}
}
补充 JS 的代码:
// <name> 换 YourFuncName,<messageBody> 换你要的入参即可
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)