iOS WKWebView&UIWebView

2018-10-26  本文已影响24人  天下林子

WKWebView

iOS8之后,苹果废弃掉UIWebView,开始使用新的新生儿WKWebView,与UIWebview相比较,是有很多优势的:

WKWebView与UIWebView的比较

UIWebViewDelegate: - webView:shouldStartLoadWithRequest:navigationType
        WKNavigationDelegate: - webView:didStartProvisionalNavigation:

UIWebViewDelegate: - webViewDidStartLoad:
WKNavigationDelegate: - webView:didCommitNavigation:

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增加的属性

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请求没有问题,但是就是不会把这两个参数传上去。那么怎么解决呢?
方法如下:

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。所以当你永久等待时,就卡死了。

如何同步获取结果
看这个,同步同步方式--->

image.png

方法如下:

配置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";
 };

Demo
遇到的坑,大神总结

上一篇下一篇

猜你喜欢

热点阅读