在上一篇文章中,我总结了UIWebView
与Native
进行交互的三种常用方式. 今天继续总结WKWebView
与Native
进行交互的方式.
苹果在iOS8以后推出了
WebKit
框架,WKWebView
用来负责网页的渲染、展示. 与UIWebView
相比,WKWebView
更加快捷, 内存占用更小, 也支持更多的HTML
特性.
一、协议拦截
(1)、JSToNative
-
逻辑: 点击HTML中的按钮, 传递参数到Native, Native展示接收到的数据
- 实现: JS调用OC并传递参数
-
JS代码:
<div style="margin-top: 20px">
<button onclick = "buttonClick()" style = "font-size: 30px; width: 300px; height: 100px" >
点我~
</button>
</div>
function buttonClick() {
var message = '获取到html按钮点击事件';
clickSuccess(message);
}
function clickSuccess(message) {
var param = 'JS传递过来了参数:1234';
JSTransferOC(message, param);
}
function JSTransferOC(message, param) {
var url = 'JSTransferOC://' + message +'?'+ param;
window.location.href = url;
}
- OC代码:
#import <WebKit/WebKit.h>
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
//!< 判断是否是约定好的scheme
if ([navigationAction.request.URL.scheme caseInsensitiveCompare:@"JSTransferOC"] == NSOrderedSame) {
[self presentAlertWithTitle:navigationAction.request.URL.host message:navigationAction.request.URL.query handler:^(UIAlertAction * _Nonnull action) {
decisionHandler(WKNavigationActionPolicyCancel);
}];
} else {
decisionHandler(WKNavigationActionPolicyAllow);
}
//!< 注意这里的 decisionHandler()必须调用, 否则会崩溃. 相当于UIWebView中的 - shouldStartLoadWithRequest 方法中 返回YES Or NO 是否允许跳转
}
- 原理:
- JS与Native定义好入口函数
- JSTransferOC()
; - OC遵循
< WKNavigationDelegate >
协议, 实现- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
代理方法, 这个代理方法在webView每次load前都会调用; - 在代理方法中, 拦截每次请求, 判断
request.url.scheme
是否是约定好的scheme
; - 如果是约定好的
scheme
, 那么Native解读url
, 读取参数;
- JS与Native定义好入口函数
(2)、Native调用JS, 并传递参数
-
逻辑: 点击
Native
登录按钮, 登录成功后, 将数据传递给JS, JS展示获取的数据. - 实现: Native将获取的数据传递给JS
-
JS代码:
<div style = "font-size: 30px;margin-top: 20px">
回调数据展示:
</div>
<div id = "returnValue" style = "font-size: 30px; border: 1px dotted; height: 100px;margin-top: 20px">
</div>
//!< OC调用JS, 并回传给JS参数, JS展示
function OCTransferJS(message, param) {
document.getElementById('returnValue').innerHTML = message + param;
}
- OC代码:
- (void)login:(UIButton *)sender {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//!< 注意: evaluateJavaScript 只有在webView全部load完成后才能执行
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"OCTransferJS('OC调用JS代码, 并传递参数;', 'OC传递来了参数:12345678')"] completionHandler:^(id _Nullable response, NSError * _Nullable error) {
}];
});
}
- 原理:
- JS和Native定义好函数
OCTransferJS
, 作为入口函数; - Native通过
-evaluateJavaScript: completionHandler:
方法, 执行一段JS代码, 传递参数, - JS展示传递来的参数;
- JS和Native定义好函数
二、WKScriptMessageHandler协议
WKScriptMessageHandler
, 官方释义为:A class conforming to the WKScriptMessageHandler protocol provides a method for receiving messages from JavaScript running in a webpage
, 为遵循了WKScriptMessageHandler
协议的类, 提供了一个用于从webpage
网页中运行的JavaScript
接收消息的方法
(1)、JSToNative
-
逻辑: 点击HTML中的按钮, 传递参数到Native, Native接收参数并展示;
- 实现: JSToNative
-
JS代码:
<div style="margin-top: 20px">
<button onclick = "WKScriptMessageHandlerBtnClick()" style = "font-size: 30px; width: 500px; height: 100px" >
WKScriptMessageHandlerBtn
</button>
</div>
//!< 通过WKScriptMessageHandler的方式进行交互
function WKScriptMessageHandlerBtnClick() {
var message = '获取到HTML按钮点击事件';
clickComplete(message);
}
function clickComplete(message) {
var param = 'JS传递来了参数:12345';
//!< 与webKit进行交互时, JS代码中要这样调用
window.webkit.messageHandlers.JSToOC.postMessage(message + ' ' + param);
}
- OC代码:
#import <WebKit/WebKit.h>
@interface WKWebViewVC ()<WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler>
//!< 为WKUserContentController添加name为'JSToOC'的 ScriptMessageHandler;
WKUserContentController *userC = [[WKUserContentController alloc] init];
[userC addScriptMessageHandler:self name:@"JSToOC"];
//!< 配置WKWebViewConfiguration;
WKWebViewConfiguration *configuretion = [[WKWebViewConfiguration alloc] init];
configuretion.userContentController = userC;
//!< 使用WKWebViewConfiguration初始化
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuretion];
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([message.name caseInsensitiveCompare:@"JSToOC"] == NSOrderedSame) {
[self presentAlertWithTitle:message.name message:message.body handler:nil];
}
}
- 原理:
- JS和Native约定好入口函数:
JSToOC
; - JS中通过
window.webkit.messageHandlers.JSToOC.postMessage(message + ' ' + param)
的方式, 调用JSToOC
函数; - OC通过
- addScriptMessageHandler: name:
方法来监听name
为JSToOC
的方法; - OC类遵循
WKScriptMessageHandler
协议, 并实现代理方法- userContentController: didReceiveScriptMessage:
, 在代理方法中获取数据message.body
;
- JS和Native约定好入口函数:
注:
- addScriptMessageHandler: name:
会引起循环引用, 应该在合适的时间去-removeScriptMessageHandlerForName:
;
(2)、NativeToJS:
和上述方法相同, 都是通过-evaluateJavaScript:completionHandler:
来执行一段JS字符串;
三、WKUIDelegate协议
经常有在群里看到朋友抱怨,
WKWebView
中, JS调用alert()
、confirm()
和prompt()
函数没有反应。 这是由于WKWebView
的特性,native
必须实现WKUIDelegate
的三个代理方法, 才能正常弹出提示框.
(1)、提示框的弹出
-
逻辑: 点击
html
中的相应按钮, 在native
中弹出提示框; - 实现: JS通过WKWebView调用弹窗
-
JS代码:
<div id = "returnValue" style = "font-size: 30px; border: 1px dotted; height: 100px;margin-top: 20px">
</div>
<div style="margin-top: 20px">
<button onclick = "alertButtonClick()" style = "font-size: 30px; width: 300px; height: 100px" >
alertButton
</button>
</div>
<div style="margin-top: 20px">
<button onclick = "confirmButtonClick()" style = "font-size: 30px; width: 300px; height: 100px" >
confirmButton
</button>
</div>
<div style="margin-top: 20px">
<button onclick = "promptButtonClick()" style = "font-size: 30px; width: 300px; height: 100px" >
promptButton
</button>
</div>
function alertButtonClick() {
var result = alert('点击了HTML中的alertButton');
document.getElementById('returnValue').innerHTML = result;
}
function confirmButtonClick() {
var result = confirm('点击了HTML中的confirmButton');
document.getElementById('returnValue').innerHTML = result;
}
function promptButtonClick() {
var result = prompt('点击了HTML中的promptButton', '确定');
document.getElementById('returnValue').innerHTML = result;
}
- OC代码:
- alert:
#pragma mark - WKUIDelegate
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
[self presentAlertWithTitle:@"title" message:message handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}];
}
- confirm:
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Confirm" message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);
}];
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(YES);
}];
[alertController addAction:cancelAction];
[alertController addAction:confirmAction];
[self presentViewController:alertController animated:YES completion:nil];
}
- TextInputPanel:
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:nil preferredStyle:UIAlertControllerStyleAlert];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = defaultText;
}];
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(alertController.textFields[0].text);
}];
[alertController addAction:confirmAction];
[self presentViewController:alertController animated:YES completion:nil];
}
- 原理:
-
html
中创建各个弹窗需要的按钮, 并调用弹窗的方法; -
native
中实现WKUIDelegate
中的代理方法, 调用原生弹窗;
-
网友评论