以前使用过WKWebView做过一个联通营业厅项目,里面包含了一些WKWebView的使用和OC与JS交互的功能,最近正好在整理这些东西,所以做一个总结吧。
本篇会涵盖如下内容
- WKWebView的基本使用
- WKWebView是如何与JS进行交互的
先看一下Demo的效果
WK的OC与JS交互.gif
WKWebview作为替代UIWebView的存在,优点还是很多的,当然坑点也是不少的(当然这些坑都是有解决办法的),苹果也在12.0以后大力推行WKWebview,使用UIWebview都开始报警告了,已经到了不得不使用它的地步了,下面开始正题。
WKWebView初始化
_wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) configuration:config];
_wkWebView.navigationDelegate = self;
_wkWebView.UIDelegate = self;
先设置WKWebViewConfiguration
,这个类主要有以下东西需要设置
- (WKWebViewConfiguration *)setConfiguration {
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.preferences = preference;
configuration.userContentController = userContentController;
return configuration;
}
- WKPreferences 设置对象
/**
设置preferences
*/
- (WKPreferences *)getPreferences {
WKPreferences *preference = [[WKPreferences alloc] init];
//设置是否支持javaScript 默认是支持的
preference.javaScriptEnabled = YES;
//是否允许javaScript自动打开窗口而不经过用户,默认为NO
preference.javaScriptCanOpenWindowsAutomatically = YES;
return preference;
}
- WKUserContentController OC与JS交互的管理类
//添加OC和JS交互的一些参数
+ (WKUserContentController *)addScriptMessageHandler:(id<WKScriptMessageHandler>)observer {
NSString *path = [[NSBundle mainBundle] pathForResource:@"ScriptHandlerList" ofType:@"plist"];
NSArray *scriptHandlerList = [NSArray arrayWithContentsOfFile:path];
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
for (NSString *key in scriptHandlerList) {
[userContentController addScriptMessageHandler:observer name:key];
}
return userContentController;
}
这里我把JS对OC的回调的方法名简单维护到了一个plist表中。
JS调OC
当然我们同样可以在decidePolicyForNavigationAction
这个代理里面去拦截url实现JS交互,但是这里我们不这么做。我们使用WKScriptHandler交互需要JS按照如下格式调用,下面是JS需要写的代码
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
简单的写了一个本地html文件用来测试
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>UIWebView中OC与JS交互</title>
</head>
<style type="text/css">
.button {
background: #f5be14;
margin: 10px;
text-align: center;
width: 300px;
height: 44px;
line-height: 44px;
margin: 10px auto;
border-radius: 5px;
}
#setImage {
width: 200px;
height: 200px;
margin: 0 auto;
}
#setText {
width: 200px;
height: 200px;
margin: 0 auto;
}
</style>
<body>
<div class="button" onclick="firstClick()">今晚打老虎</div>
<div class="button" onclick="secondClick()">请用力的点我</div>
<div class="button" onclick="thirdClick()">弹我弹我</div>
<div class="button" onclick="forthClick()">选择图片</div>
<div class="button" onclick="callOCToCallJSClick()">调用OC执行JS来连接两个字符串</div>
<div id="setImage"></div>
<div id="setText"></div>
</body>
<script type="text/javascript">
function getText(index) {
return document.getElementsByClassName("button")[index].innerText;
}
function firstClick() {
var action = "firstClick";
var token = getText(0);
var paras = getParams(action, token);
window.webkit.messageHandlers.firstClick.postMessage(paras);
}
function secondClick() {
var action = "secondClick";
var token = getText(1);
var paras = getParams(action, token);
window.webkit.messageHandlers.secondClick.postMessage(paras);
}
function thirdClick() {
var action = "thirdClick";
var token = getText(2);
var paras = getParams(action, token);
window.webkit.messageHandlers.thirdClick.postMessage(paras);
}
function forthClick() {
var action = "forthClick";
var token = getText(3);
var paras = getParams(action, token);
window.webkit.messageHandlers.forthClick.postMessage(paras);
}
function callOCToCallJSClick() {
var action = "callOCToCallJSClick";
var token = getText(4);
var paras = getParams(action, token);
window.webkit.messageHandlers.callOCToCallJSClick.postMessage(paras);
}
function getParams(action, token) {
var paras = {
'action' : action,
'token' : token
}
return paras;
}
function showImageOnDiv(imageStr) {
var imgDiv = document.getElementById("setImage");
imgDiv.innerHTML = "<image style='width:200px;' src='data:image/png;base64,"+imageStr+"'>";
}
function ocToJS(str1, str2) {
var str = "str1" + " OCTOJS " + "str2";
var textDiv = document.getElementById("setText");
textDiv.innerHTML = str;
}
</script>
</html>
上面是JS调用OC的代码,而OC这边接收需要遵守协议WKScriptMessageHandler
,实现代理方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSDictionary *dic = message.body;
//进行OC方法调用
SEL sel = NSSelectorFromString([NSString stringWithFormat:@"%@:", dic[@"action"]]);
objc_msgSend(self, sel, dic[@"token"]);
}
OC调用JS
OC调用JS使用WKWebView的evaluateJavaScript
方法就行。
NSString *jsFunctStr = [NSString stringWithFormat:@"showImageOnDiv('%@')",imageString];
//OC调用JS
[self.wkWebView evaluateJavaScript:jsFunctStr completionHandler:^(id _Nullable name, NSError * _Nullable error) {
NSLog(@"完事儿");
}];
WKNavigationDelegate
这个代理有下列协议方法
#pragma mark - WKNavigationDelegate
/**
根据url来判断webview是否跳转到外部链接
*/
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSURL *url = navigationAction.request.URL;
// NSString *urlString = url ? url.absoluteString:@"";
[self wkWebViewWillStart:url];
decisionHandler(WKNavigationActionPolicyAllow);
}
/**
根据服务器返回的响应头判断是否可以跳转外链
*/
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
decisionHandler(WKNavigationResponsePolicyAllow);
}
/**
页面开始加载
*/
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
}
/**
页面加载失败
*/
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
}
/**
当页面内容开始返回时调用
*/
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
}
/**
页面加载完成时调用
*/
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(@"加载了一次");
}
WKUIDelegate
这个代理有五个协议方法,基本上就是处理JavaScript的提示框的样式,应对一些提示框要源生来处理的业务要求,可以说很贴心了,Demo并没有做这个处理。
/* 警告 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
[[[UIAlertView alloc] initWithTitle:@"警告框" message:message delegate:nil cancelButtonTitle:@"确认" otherButtonTitles: nil] show];
completionHandler();
}
///** 确认框 */
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{
[[[UIAlertView alloc] initWithTitle:@"确认框" message:message delegate:nil cancelButtonTitle:@"确认" otherButtonTitles: nil] show];
completionHandler(YES);
}
/** 输入框 */
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler{
[[[UIAlertView alloc] initWithTitle:@"输入框" message:prompt delegate:nil cancelButtonTitle:@"确认" otherButtonTitles: nil] show];
completionHandler(@"哈哈哈");
}
以上,就是WKWebView使用WKScriptHandler来进行JS交互的笔记整理。