美文网首页
iOS WKWebView&UIWebView

iOS WKWebView&UIWebView

作者: 天下林子 | 来源:发表于2018-10-26 18:23 被阅读24次

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

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

    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
    遇到的坑,大神总结

    相关文章

      网友评论

          本文标题:iOS WKWebView&UIWebView

          本文链接:https://www.haomeiwen.com/subject/bjultqtx.html