iOS WKWebView&UIWebView
WKWebView
iOS8之后,苹果废弃掉UIWebView,开始使用新的新生儿WKWebView,与UIWebview相比较,是有很多优势的:
- 更多的支持Html5的特性
- 官方宣称的高达60fps的滚动刷新率以及内置手势
- 将UIWebviewDelegate与UIWebView拆分。可以参考Webkit
- Safari相同的JavaScript引擎
- 占用更少的内存
WKWebView与UIWebView的比较
- 准备加载页面
UIWebViewDelegate: - webView:shouldStartLoadWithRequest:navigationType
WKNavigationDelegate: - webView:didStartProvisionalNavigation:
- 内容开始加载 -view的过渡动画可在此方法中加载
UIWebViewDelegate: - webViewDidStartLoad:
WKNavigationDelegate: - webView:didCommitNavigation:
- 页面加载完成- view的过渡动画的移除可在此方法中进行
UIWebViewDelegate: - webViewDidFinishLoad:
WKNavigationDelegate: - webView:didFinishNavigation:
- 页面加载失败
UIWebViewDelegate: - webView:didFailLoadWithError:
WKNavigationDelegate: - webView:didFailNavigation:withError:
WKNavigationDelegate: - webView:didFailProvisionalNavigation:withError:
WKWebView还有三个页面跳转的代理方法
- 接收到服务器跳转请求的代理
WKNavigationDelegate: - webView:didReceiveServerRedirectForProvisionalNavigation:
- 在收到响应后,决定是否跳转的代理
WKNavigationDelegate: - webView:decidePolicyForNavigationResponse:decisionHandler:
- 在发送请求之前,决定是否跳转的代理
WKNavigationDelegate: - webView:decidePolicyForNavigationAction:decisionHandler:
WKWebView增加的属性
- WKWebViewConfiguration *configuration: 初始化WKWebView的时候的配置,后面会用到
- WKBackForwardList *backForwardList: 相当于访问历史的一个列表
- double estimatedProgress: 进度, 有这个之后就不用自己写假的进度条了
WKWebView的使用
WKWebView有两个delegate,WKUIDelegate 和 WKNavigationDelegate
WKNavigationDelegate主要处理一些跳转,加载处理操作,WKUIDelegate主要处理JS脚本,确认框,警告框等,所以WKNavigationDelegate更加常用
代理的主要方法
#pragma mark - WKNavigationDelegate
// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
}
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{
}
// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
}
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{
}
// 接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
}
// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
NSLog(@"%@",navigationResponse.response.URL.absoluteString);
//允许跳转
decisionHandler(WKNavigationResponsePolicyAllow);
//不允许跳转
//decisionHandler(WKNavigationResponsePolicyCancel);
}
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
NSLog(@"%@",navigationAction.request.URL.absoluteString);
//允许跳转
decisionHandler(WKNavigationActionPolicyAllow);
//不允许跳转
//decisionHandler(WKNavigationActionPolicyCancel);
}
#pragma mark - WKUIDelegate
// 创建一个新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{
return [[WKWebView alloc]init];
}
// 输入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler{
completionHandler(@"http");
}
// 确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{
completionHandler(YES);
}
// 警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
NSLog(@"%@",message);
completionHandler();
}
WKWebView 和 JS交互
在WebKit框架中,有WKWebView可以替换UIKit的UIWebView和AppKit的WebView,而且提供了在两个平台可以一致使用的接口。WebKit框架使得开发者可以在原生App中使用Nitro来提高网页的性能和表现,Nitro就是Safari的JavaScript引擎,WKWebView不支持JavaScriptCore的方式但提供message handler的方式为JavaScript与Native通信
核心代码
OC调用JS方法
OC代码
原生调用JS的代码需要在页面加载完成之后,就是在-webView:didFinishNavigation:代理方法里面
#pragma mark - 生命周期 Life Circle
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"WKWebView调用JS";
[self _testJS];
}
#pragma mark -
- (void)_testJS
{
self.myWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0,210+40, kScreenW, kScreenH - 221) configuration:[[WKWebViewConfiguration alloc] init]];
self.myWebView.UIDelegate = self;
[self.view addSubview:self.myWebView];
self.someString = @"iOS 8引入了一个新的框架——WebKit,之后变得好起来了。在WebKit框架中,有WKWebView可以替换UIKit的UIWebView和AppKit的WebView,而且提供了在两个平台可以一致使用的接口。WebKit框架使得开发者可以在原生App中使用Nitro来提高网页的性能和表现,Nitro就是Safari的JavaScript引擎 WKWebView 不支持JavaScriptCore的方式但提供message handler的方式为JavaScript与Native通信";
[self loadTouch:nil];
}
//刷新html
- (IBAction)loadTouch:(id)sender {
[self loadHtml:@"WKWebViewJS"];
}
//执行已经存在的js代码
- (IBAction)exeFuncTouched:(id)sender {
[self.myWebView evaluateJavaScript:@"showAlert('hahaha')" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"--执行已经存在的js代码-%@", result);
}];
}
//自带标签getElementsByTagName插入html
- (IBAction)insertHtmlTouch:(id)sender {
//替换第一个P元素内容
NSString *tempString = [NSString stringWithFormat:@"document.getElementsByTagName('p')[0].innerHTML='%@';", self.someString];
[self.myWebView evaluateJavaScript:tempString completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"--自带标签getElementsByTagName插入html-%@", result);
}];
}
//getElementsByName 根据标签名称获取定位元素 填input
- (IBAction)inputButton:(id)sender {
NSString *tempString = [NSString stringWithFormat:@"document.getElementsByName('wd')[0].value='%@';", self.someString];
[self.myWebView evaluateJavaScript:tempString completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"--根据标签名称获取定位元素 填input-%@", result);
}];
}
//getElementById插入html
- (IBAction)insertDivHtml:(id)sender {
//替换第id为idtest的DIV元素内容
NSString *tempString2 = [NSString stringWithFormat:@"document.getElementById('idTest').innerHTML='%@';", self.someString];
[self.myWebView evaluateJavaScript:tempString2
completionHandler:^(id _Nullable rr, NSError * _Nullable error) {
NSLog(@"--自带标签getElementsByTagName插入html->>>>>>>>>>>>>>>>>>>>>>>>%@", rr);
}];
}
//插入JS并且执行
- (IBAction)insertJS:(id)sender {
NSString *insertString = [NSString stringWithFormat:@"var script = document.createElement('script');""script.type = 'text/javascript';""script.text = \"function jsFunc() { ""var a=document.getElementsByTagName('body')[0];""alert('%@');""}\";""document.getElementsByTagName('head')[0].appendChild(script);", self.someString];
// NSString *insertString = [NSString stringWithFormat:
// @"var script = document.createElement('script');"
// "script.type = 'text/javascript';"
// "script.text = \"function jsFunc() { "
// "var a=document.getElementsByTagName('body')[0];"
// "alert('%@');"
// "}\";"
// "document.getElementsByTagName('head')[0].appendChild(script);", self.someString];
NSLog(@"++++++++++insert string %@",insertString);
[self.myWebView evaluateJavaScript:insertString completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"+++++++1111111+++insert string %@",result);
}];
[self.myWebView evaluateJavaScript:@"jsFunc();" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"++++22222++++++insert string %@",result);
}];
}
//修改字体
- (IBAction)fontTouched:(id)sender {
NSString *str = [NSString stringWithFormat:@"document.getElementsByTagName('p')[0].style.fontSize='%@';",@"19px"];
[self.myWebView evaluateJavaScript:str completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"++++修改字体++++++ %@",result);
}];
}
//替换图片地址
- (IBAction)replaceImgSrc:(id)sender {
NSString *str = [NSString stringWithFormat:@"document.getElementsByTagName('img')[0].src = '%@';",@"light_advice.png"];
[self.myWebView evaluateJavaScript:str completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"++++替换图片地址++++++ %@",result);
}];
}
//submit
- (IBAction)submitTouched:(id)sender {
}
#pragma mark - private
- (void)loadHtml:(NSString *)name
{
NSString *filePath = [[NSBundle mainBundle] pathForResource:name ofType:@"html"];
NSURL *url = [NSURL fileURLWithPath:filePath];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.myWebView loadRequest:request];
}
#pragma mark - WKUIDelegate
- (void)webViewDidClose:(WKWebView *)webView
{
NSLog(@">>>>>>>>>%s\n", __FUNCTION__);
}
//UIWebView 中这个方法时私有方法 通过category可以拦截alert
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
NSLog(@"++++++++%s\n", __FUNCTION__);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:@"1024-1024" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}]];
[self presentViewController:alert animated:YES completion:nil];
}
html代码
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width,target-densitydpi=high-dpi,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<style type="text/css">
.detail{
color:#787878;
font-size: 14px;
}
.detail p{
text-indent:2em;
}
.pic img{
width:113px;
height:20px;
}
</style>
<script type="text/javascript">
function showAlert(hahahha) {
alert(hahahha);
}
</script>
</head>
<body>
<div id="idTest">
<p>
</p>
</div>
<form method="get" action="http://www.baidu.com/s">
<input type="text" id = "wd" name ="wd" value="123"/>
<input type="submit" id ="submitButton" name ="submitButton" value="提交"/>
</form>
<div class="detail">
<div class = "pic"><img src="light_illstration.png"></div>
<p>
this is test。
</p>
</div>
<div class="detail">
<div class = "pic"><img src="light_advice.png"></div>
<p>
这是一个测试。
</p>
</div>
</body>
</html>
JS调用OC
代码如下:
JSWKWebView.html中
<!--<!DOCTYPE html>-->
<!--<html>-->
<!-- <meta http-equiv="content-Type" content="text/html; charset=UTF-8">-->
<!-- <meta name="viewport" content="width=device-width,target-densitydpi=high-dpi,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>-->
<!-- <head>-->
<!-- -->
<!-- <title></title>-->
<!-- <script type="text/javascript">-->
<!-- //本方法兼容安卓与iOS-->
<!-- function callMobile(handlerInterface,handlerMethod,parameters){-->
<!-- //handlerInterface 由iOS addScriptMessageHandler与andorid addJSinterface代码c注册而来-->
<!-- var dic = {'handlerInterface':handlerInterface,'funcation':handlerMethod,'parameters':parameters};-->
<!-- if(/(iphone|iPad|iPod)iOS)/i.test(navigator.userAgent)){-->
<!-- window.webkit.messageHandlers[handlerInterface].postMessage(dic);-->
<!-- }else{-->
<!-- //安卓传输不了js json对象-->
<!-- window[handlerInterface][handlerMethod](JSON.stringify(dic));-->
<!-- }-->
<!-- }-->
<!-- function callMobileNative(handlerInterface,handlerMethod,parameters){-->
<!-- callMobile("Native",handlerMethod,parameters);-->
<!-- }-->
<!-- -->
<!-- function callFunc(){-->
<!-- var stack = new Array();-->
<!-- stack["first"] = 3;-->
<!-- stack["second"] = "second";-->
<!-- stack["third"] = new Date();-->
<!-- callMobile("Native","callFunc",stack);-->
<!-- }-->
<!-- function testAlert(){-->
<!-- alert("捕获了webView的alert")-->
<!-- }-->
<!-- </script>-->
<!-- </head>-->
<!-- <body>-->
<!-- <br>-->
<!-- <br>-->
<!-- <br>-->
<!-- <br>-->
<!-- <input type="button" value="js callFunc" onclick="callFunc()" />-->
<!-- -->
<!-- <input type="button" value="">-->
<!-- <input type="button" value="打个招呼" onclick="callMobile('Native','alert',{'message':'你好么'})" />-->
<!-- -->
<!-- <input type="button" value="add view" onclick=“callMobile(‘Native’,'addsubView',{'view':'UIWebView','tag':'11111','urlstring':'http://www.mob.com','frame':'{{0,200},{320,100}}'})” />-->
<!-- -->
<!-- <input type="button" value="传个字典" onclick="callMobile(‘Native’,'testFunc',{'param1':76,'param2':155,'param3':76})" />-->
<!-- -->
<!-- <input type="button" value="Pay协议逻辑" onclick="callMobile('Pay','testFunc',{'param1':76,'param2':155,'param3':76})" />-->
<!-- -->
<!-- <input type="button" value="js中弹出原生alert" onclick="testAlert()" />-->
<!-- -->
<!-- </body>-->
<!-- -->
<!-- -->
<!-- -->
<!-- -->
<!---->
<!--</html>-->
<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width,target-densitydpi=high-dpi,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<head>
<title></title>
<script type="text/javascript">
//本方法兼容安卓与iOS
function callMobile(handlerInterface,handlerMethod,parameters){
//handlerInterface由iOS addScriptMessageHandler与andorid addJavascriptInterface 代码注入而来。
var dic = {'handlerInterface':handlerInterface,'function':handlerMethod,'parameters': parameters};
if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)){
window.webkit.messageHandlers[handlerInterface].postMessage(dic);
}else{
//安卓传输不了js json对象
window[handlerInterface][handlerMethod](JSON.stringify(dic));
}
}
function callMobileNative(handlerInterface,handlerMethod,parameters){
callMobile("Native",handlerMethod,parameters);
}
function callFunc(){
var stack = new Array();
stack["first"] = 3;
stack["second"] = "second";
stack["third"] = new Date();
callMobile("Native","callFunc",stack);
}
function testAlert(){
alert("捕获了webview的alert");
}
</script>
</head>
<body>
<br>
<br>
<br>
<br>
<br>
<br>
<input type="button" value="js callFunc" onclick="callFunc()" />
<input type="button" value="打个招呼" onclick="callMobile('Native','alert',{'message':'你好么'})" />
<input type="button" value="add个view" onclick="callMobile('Native','addSubView',{'view':'UIWebView','tag':'11111','urlstring':'http://www.baidu.com','frame':'{{0,200},{320,100}}'})" />
<input type="button" value="传个字典" onclick="callMobile('Native','testFunc',{'param1':76,'param2':155,'param3':76})" />
<input type="button" value="Pay协议逻辑" onclick="callMobile('Pay','testFunc',{'param1':76,'param2':155,'param3':76})" />
<input type="button" value="js中弹出原生alert" onclick="testAlert()" />
</body>
</html>
OC代码:
#pragma mark - 生命周期 Life Circle
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"JS调用WKWebView";
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = [[WKUserContentController alloc] init];
//注入js对象Native
//声明WKScriptMessageHandler协议
[config.userContentController addScriptMessageHandler:self name:@"Native"];
[config.userContentController addScriptMessageHandler:self name:@"pay"];
self.myWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
self.myWebView.UIDelegate = self;
[self.view addSubview:self.myWebView];
[self loadBtn:nil];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
ViewController *vc = [sb instantiateViewControllerWithIdentifier:@"ViewController"];
[self.navigationController pushViewController:vc animated:YES];
}
- (IBAction)loadBtn:(id)sender {
[self _loadHtml:@"JSWKWebView"];
}
- (void)_loadHtml:(NSString *)name
{
NSString *filePath = [[NSBundle mainBundle] pathForResource:name ofType:@"html"];
NSURL *url = [NSURL fileURLWithPath:filePath];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.myWebView loadRequest:request];
}
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
NSDictionary *bodyParam = (NSDictionary *)message.body;
NSString *func = [bodyParam objectForKey:@"function"];
NSLog(@"---------- MessageHandler name : %@", message.name);
NSLog(@"---------- MessageHandler Body : %@", message.body);
NSLog(@"---------- MessageHandler function : %@", func);
if ([message.name isEqualToString:@"Native"]) {
NSDictionary *parameters = [bodyParam objectForKey:@"parameters"];
//调用本地函数1
if ([func isEqualToString:@"addsubView"]) {
Class tempClass = NSClassFromString([parameters objectForKey:@"view"]);
CGRect frame = CGRectFromString([parameters objectForKey:@"frame"]);
if (tempClass && [tempClass isSubclassOfClass:[WKWebView class]]) {
WKWebView *tempObj = [[tempClass alloc] initWithFrame:frame ];
tempObj.tag = [[parameters objectForKey:@"tag"] integerValue];
NSURL *url = [NSURL URLWithString:[parameters objectForKey:@"urlstring"]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[tempObj loadRequest:request];
//[self.myWebView addSubview:tempObj];
}
}
else if ([func isEqualToString:@"alert"])
{//调用本地函数2
[self showMessage:@"来自网页提示" message:[parameters description]];
}
else if ([func isEqualToString:@"callFunc"])
{
}
else if([message.name isEqualToString:@"Pay"])
{
}
else if ([message.name isEqualToString:@"dosomething"])
{
}
}
}
- (void)showMessage:(NSString *)title message:(NSString *)message
{
if (message == nil) {
return;
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
[alert show];
}
#pragma mark - WKUIDelegate
- (void)webViewDidClose:(WKWebView *)webView
{
NSLog(@"-----%s", __FUNCTION__);
}
//UIWebView 中这个方法是私有方法, 通过category 可以拦截alert
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
NSLog(@"--%s", __FUNCTION__);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
ViewController *vc = [sb instantiateViewControllerWithIdentifier:@"ViewController"];
[self.navigationController pushViewController:vc animated:YES];
}]];
[self presentViewController:alert animated:YES completion:nil];
}
替换WKWebView 遇到的问题
解决WKWebView加载POST请求无法发送参数问题
代码如下:
// 创建WKWebView
WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 设置访问的URL
NSURL *url = [NSURL URLWithString:@"http://www.example.com"];
// 根据URL创建请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 设置请求方法为POST
[request setHTTPMethod:@"POST"];
// WKWebView加载请求
[webView loadRequest:request];
// 将WKWebView添加到视图
[self.view addSubview:webView];
在上面的基础上如果进行添加参数请求如下面
// 设置请求参数
[request setHTTPBody:[@"username=aaa&password=123" dataUsingEncoding:NSUTF8StringEncoding]];
你会发现POST请求没有问题,但是就是不会把这两个参数传上去。那么怎么解决呢?
方法如下:
- 将一个包含JavaScript的POST请求的HTML代码放到工程目录中
- 加载这个包含JavaScript的POST请求的代码到WKWebView
- 加载完成之后,用Native调用JavaScript的POST方法并传入参数来完成请求
html代码:
<html>
<head>
<script>
//调用格式: post('URL', {"key": "value"});
function post(path, params) {
var method = "post";
var form = document.createElement("form");
form.setAttribute("method", method);
form.setAttribute("action", path);
for(var key in params) {
if(params.hasOwnProperty(key)) {
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", key);
hiddenField.setAttribute("value", params[key]);
form.appendChild(hiddenField);
}
}
document.body.appendChild(form);
form.submit();
}
</script>
</head>
<body>
</body>
<body>
<br>
<br>
<br>
<br>
<br>
<br>
<input type="button" value="js callFunc" onclick="callFunc()" />
<input type="button" value="打个招呼" onclick="callMobile('Native','alert',{'message':'你好么'})" />
</body>
</html>
OC代码:
@interface MOBJSPostViewController ()<WKUIDelegate,WKNavigationDelegate>
@property (nonatomic, assign) BOOL needLoadJSPOST;
@property (nonatomic, strong) WKWebView *webView;
@end
@implementation MOBJSPostViewController
#pragma mark - 生命周期 Life Circle
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"发送POST请求";
[self _testJS];
}
- (void)_testJS
{
// JS发送POST的Flag,为真的时候会调用JS的POST方法(仅当第一次的时候加载本地JS)
self.needLoadJSPOST = YES;
//创建WKWebView
WKWebViewConfiguration * config = [[WKWebViewConfiguration alloc] init];
self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
//获取js所在的路径
NSString *path = [[NSBundle mainBundle] pathForResource:@"JSPost" ofType:@"html"];
NSString *html = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[self.webView loadHTMLString:html baseURL: [NSURL fileURLWithPath:path]];
self.webView.UIDelegate = self;
self.webView.navigationDelegate = self;
[self.view addSubview:self.webView];
}
// 加载完成的代理方法
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(@"----->>>>>>>>>>>>>");
// 判断是否需要加载(仅在第一次加载)
if (self.needLoadJSPOST) {
// 调用使用JS发送POST请求的方法
[self postRequestWithJS];
// 将Flag置为NO(后面就不需要加载了)
self.needLoadJSPOST = NO;
}
}
// 调用JS发送POST请求
- (void)postRequestWithJS {
// 发送POST的参数
NSString *postData = @"\"username\":\"aaa\",\"password\":\"123\"";
// 请求的页面地址
NSString *urlStr = @"http://www.postexample.com";
// 拼装成调用JavaScript的字符串
NSString *jscript = [NSString stringWithFormat:@"post('%@', {%@});", urlStr, postData];
NSLog(@">>>>>>>>>>>>>Javascript: %@", jscript);
// 调用JS代码
[self.webView evaluateJavaScript:jscript completionHandler:^(id object, NSError * _Nullable error) {
}];
}
记录一个问题
evaluateJavaScript:completionHandler:异步
该方法是异步回调,这个一看方法的声明便知。可能有小伙伴就是需要同步获取返回值,有没有办法呢?
可能你会说用信号量dispatch_semaphore_t。好吧,可能你会这么写~
__block id cookies;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.webView evaluateJavaScript:@"document.cookie" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
cookies = result;
dispatch_semaphore_signal(semaphore);
}];
//等待三秒,接收参数
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC));
//打印cookie,肯定为空,因为足足等了3s,dispatch_semaphore_signal都没有起作用
NSLog(@"cookie的值为:%@", cookies);
等待了3s,如果你等待DISPATCH_TIME_FOREVER,👀,程序不会Crash,但界面卡死了。结果是,NSLog的触发时间要早于completionHandler回调,不论你等多久,它都会打印null。所以当你永久等待时,就卡死了。
如何同步获取结果
看这个,同步同步方式--->
方法如下:
配置WKWebView
//创建webView
-(void)creatWebView
{
self.group = dispatch_semaphore_create(0);
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
//初始化偏好设置属性:preferences
config.preferences = [WKPreferences new];
//The minimum font size in points default is 0;
config.preferences.minimumFontSize = 10;
//是否支持JavaScript
config.preferences.javaScriptEnabled = YES;
//不通过用户交互,是否可以打开窗口
config.preferences.javaScriptCanOpenWindowsAutomatically = NO;
//通过JS与webView内容交互
config.userContentController = [WKUserContentController new];
// 注入JS对象名称senderModel,当JS通过senderModel来调用时,我们可以在WKScriptMessageHandler代理中接收到
[config.userContentController addScriptMessageHandler:self name:@"senderModel"];
[config.userContentController addScriptMessageHandler:self name:@"call"];
self.webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, 375, 300+40) configuration:config];
NSURL *path = [[NSBundle mainBundle] URLForResource:@"WKWebViewText" withExtension:@"html"];
[self.webView loadRequest:[NSURLRequest requestWithURL:path]];
[self.view addSubview:self.webView];
self.webView.navigationDelegate = self;
self.webView.UIDelegate = self;
[self.webView evaluateJavaScript:@"$mob = function () {}; $mob.native = function () {};" completionHandler:nil];
//MARK:- >>>>>>>>>>>>>>>>注册>>>>>>>>>>>>>>>>
[self.webView evaluateJavaScript:
@"$mob.native.base64Decode = function ()\
{\
var args = arguments;\
var type = \"Testbridge\";\
var name = \"$mob.native.base64Decode\";\
var data = {\"args\":args};\
var payload = {\"type\":type, \"functionName\":name, \"data\":args};\
var res = prompt(JSON.stringify(payload));\
return res;\
}"
completionHandler:nil];
}
执行
//MARK:- 调用-------
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.webView evaluateJavaScript:@"$mob.native.base64Decode('123','456');" completionHandler:^(id _Nullable rr, NSError * _Nullable error) {
NSLog(@"-----%@", rr);
}];
}
代理方法
#pragma mark - --------获取数据 ---------
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {
NSData *dataFromString = [prompt dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO];
if (dataFromString)
{
//NSData *jsonData = [dataFromString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:dataFromString options:NSJSONReadingMutableContainers error:nil];
NSString *type = [payload objectForKey:@"type"];
if ([type isEqualToString:@"Testbridge"])
{
NSString *functionName = [payload objectForKey:@"functionName"];
if ([functionName isEqualToString:@"$mob.native.base64Decode"])
{
// 1.解码bo
// 2.解码结果回调
//completionHandler(@"我是回调~ .....");
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:[payload objectForKey:@"data"]
options:NSJSONWritingPrettyPrinted
error:&error];
NSString *jsonString = @"";
if (!jsonData)
{
NSLog(@"Got an error: %@", error);
}
else
{
// jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
jsonString = [[NSString alloc] initWithData:dataFromString encoding:NSUTF8StringEncoding];
}
NSData *data = [self dataByBase64DecodeString:jsonString];
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//return [JSValue valueWithObject:str inContext:theContext];
completionHandler(@"我是回调~ .....");
//completionHandler(str);
}
NSData *data = [payload objectForKey:@"data"];
NSLog(@"---data---%@", data);
//return;
}
else
{
}
}
// UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"输入框" message:@"调用输入框" preferredStyle:UIAlertControllerStyleAlert];
// [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
// textField.textColor = [UIColor blackColor];
// }];
//
// [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
// completionHandler([[alert.textFields lastObject] text]);
// }]];
//
// [self presentViewController:alert animated:YES completion:NULL];
}
这样就ok了,老板可以自己试下
UIWebView 的
js调用OC方法且有返回值
UIWebView:
JSContext *jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
jsContext[@"sayhi"] = ^(NSString *name) {
NSLog(@"say hi to %@",name);
return "say hi to xxxx";
};