美文网首页
WKWebView替换UIWebView

WKWebView替换UIWebView

作者: 人仙儿a | 来源:发表于2017-08-16 19:13 被阅读26次

    1.引入头文件

    #import <WebKit/WebKit.h>
    

    2.先使用WKWebView基本方法写个例子,加载baidu页面,写法跟UIWebView类似

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
        
        _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, self.view.width, self.view.height) configuration:config];
        [self.view addSubview:_webView];
        
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]];
        [_webView loadRequest:request];
    }
    

    用同样的方法,用UIWebView也写了一个页面做对比,发现差距好大,WKWebView内存是25M,而UIWebView是98M。之前没发现UIWebView内存会占如何之大,WKWebView看来很值!!!

    3.监听加载的进度,因为没有“加载进度”相关的delegate回调

    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        [self.webView addObserver:self
                       forKeyPath:@"loading"
                          options:NSKeyValueObservingOptionNew
                          context:nil];
        [self.webView addObserver:self
                       forKeyPath:@"title"
                          options:NSKeyValueObservingOptionNew
                          context:nil];
        [self.webView addObserver:self
                       forKeyPath:@"estimatedProgress"
                          options:NSKeyValueObservingOptionNew
                          context:nil];
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        if ([keyPath isEqualToString:@"loading"]) {
            NSLog(@"loading--%d", _webView.loading);
        } else if ([keyPath isEqualToString:@"title"]) {
            NSLog(@"title--%@", _webView.title);
        } else if ([keyPath isEqualToString:@"estimatedProgress"]) {
            NSLog(@"progress--%lf", _webView.estimatedProgress);
        }
    }
    
    
    - (void)viewWillDisappear:(BOOL)animated {
        [super viewWillDisappear:animated];
        [_webView removeObserver:self forKeyPath:@"loading"];
        [_webView removeObserver:self forKeyPath:@"title"];
        [_webView removeObserver:self forKeyPath:@"estimatedProgress"];
    }
    

    记得最后移除KVO

    日志如下:

    2017-08-16 16:35:33.833 TestSSSSS[22084:917889] progress--0.300000
    2017-08-16 16:35:33.845 TestSSSSS[22084:917889] title--百度一下
    2017-08-16 16:35:34.165 TestSSSSS[22084:917889] progress--0.409694
    2017-08-16 16:35:34.454 TestSSSSS[22084:917889] progress--0.845262
    2017-08-16 16:35:34.472 TestSSSSS[22084:917889] progress--1.000000
    2017-08-16 16:35:34.473 TestSSSSS[22084:917889] loading--0
    

    在加载结束前取得title,最后结束时progress为1,loading为NO

    4.为了方便测试,我自己写了一个html文件,页面上有个按钮,然后点击按钮弹出alert,代码如下

    <html>
    <head>
    <script>
    function postMsg() {
        alert("hehe");
    }
    </script>
    </head>
    <body>
    
    <input type='button' value='method1' onclick='postMsg()'/>
    </body>
    </html>
    

    但是奇怪的是alert怎么也弹不出来,听说WKWebView坑很多,要慎入,现在终于碰到第一个了,
    解决方法如下:

    • 1.实现两个代理
    _webView.UIDelegate = self;
    _webView.navigationDelegate = self;
    
    • 2.具体实现三个方法
    - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
        [alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            completionHandler();
        }])];
        [self presentViewController:alertController animated:YES completion:nil];
        
    }
    
    - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
        //    DLOG(@"msg = %@ frmae = %@",message,frame);
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
        [alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            completionHandler(NO);
        }])];
        [alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            completionHandler(YES);
        }])];
        [self presentViewController:alertController animated:YES completion:nil];
    }
    - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
        [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
            textField.text = defaultText;
        }];
        [alertController addAction:([UIAlertAction actionWithTitle:@"完成" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            completionHandler(alertController.textFields[0].text?:@"");
        }])];
        
        [self presentViewController:alertController animated:YES completion:nil];
    }
    

    5.js调用oc方法 重点哦!

    • js里调用方法
     window.webkit.messageHandlers.message.postMessage("hahahaha");
    
    • oc使用方法, config里的userContentController专门处理js的调用
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
       config.userContentController = [[WKUserContentController alloc] init];
       [config.userContentController addScriptMessageHandler:self name:@"message"];
    
    • 接收js事件用法
    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
      if ([message.name isEqualToString:@"message"]) {
          NSLog(@"body--%@", message.body);
          //处理js事件...
      }
    }
    

    这种写法完美的实现了js与oc的无缝连接,有点类似于服务器用的redis, 通过webKit的消息进行传递。

    • remove scriptMessage, 最后要移除,千万别忘了,否则内存泄漏。

    • (void)dealloc {
      [_webView.configuration.userContentController removeScriptMessageHandlerForName:@"message"];
      }

    6.WKWebView加载过程代理方法:

     
     #pragma mark WKNavigationDelegate
    - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
        NSLog(@"start");
    }
    
    - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
        NSLog(@"commit");
    }
    
    - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
        NSLog(@"finish");
    }
    
    - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
        NSLog(@"fail");
    }
    

    7.页面跳转代理方法, 可用于拦截URL

    // 接收到服务器跳转请求之后调用
    - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
        NSLog(@"recieve erdirect");
    }
    
    // 在收到响应后,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
        decisionHandler(WKNavigationResponsePolicyAllow);
    }
    // 在发送请求之前,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
    

    8.OC调用JS方法

    WKWebView自带的evaluateJavaScript方法

    [_webView evaluateJavaScript:@"ocInvockMsg()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
                //result 是方法的返回值
                NSLog(@"result-%@", result);
            }];
    
    WebViewJavascriptBridge第三方库 github上有9000+星,但是我觉得它适应于UIWebView,WKWebView感觉不太需要,而且WebViewJavascriptBridge需要在html添加初始化代码,侵入性太强,代码如下:
    <script type="text/javascript">
        
        
        //这是必须要写的,用来初始化一些设置
        function setupWebViewJavascriptBridge(callback) {
            if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
            if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
            window.WVJBCallbacks = [callback];
            var WVJBIframe = document.createElement('iframe');
            WVJBIframe.style.display = 'none';
            WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
            document.documentElement.appendChild(WVJBIframe);
            setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
        }
    
    
    //这也是固定的, OC 调JS , 需要给OC调用的函数必须写在这个函数里面
    setupWebViewJavascriptBridge(function(bridge) {
                                 
                                 bridge.registerHandler('testJSFunction', function(data, responseCallback) {
                                                        alert('JS方法被调用:'+data);
                                                        responseCallback('js执行过了');
                                                        })
                                 
                                 
                                 })
                                 
                                 
                                 //这个 shareClick 就是 原生那边 注入的函数 , 通过这个方法来调用原生 和传值
                                 //parmas 是JS 给OC的数据 , response 是 OC函数被调用之后 再 告诉JS 我被调用了
                                 //比如微信分享,给Dic给原生 , 原生分享成功后再把结果回调给JS 进行处理
                                 function shareClick() {
                                     var params = {'title':'测试分享的标题','content':'测试分享的内容','url':'http://www.baidu.com'};
                                     WebViewJavascriptBridge.callHandler('shareClick',params,function(response) {
                                                                         
                                                                         console.log(response);
                                                                         
                                                                         
                                                                         });
                                 }
    
    
    
    </script>
    

    webBridge初始化及注册用于js调用oc的函数

        _bridge = [WebViewJavascriptBridge bridgeForWebView:_webView];
        [_bridge setWebViewDelegate:self];
        [WebViewJavascriptBridge enableLogging];
        [_bridge registerHandler:@"shareClick" handler:^(id data, WVJBResponseCallback responseCallback) {
            NSLog(@"share---");
        }];
    

    oc调用js的函数,如下:

    [_bridge callHandler:@"testJSFunction" data:@"aa" responseCallback:^(id responseData) {
                NSLog(@"responseData--%@", responseData);
            }];
    

    综上所述,WebViewJavascriptBridge使用非常方便,但是前台html的业务量上升,不仅要添加初始化方法,并在刚开始时调用一次,而且还要在html里使用registerHandler和callHandler这两个方法来注册和调用方法,感觉Html里侵入性太强了,我要是做前端的我就疯了,因为它的代码还在网站上显示啊,不光是用在iOS/android上呀,而且你们iOS出了个新框架,让我前端跟着变,你当我傻???

    相关文章

      网友评论

          本文标题:WKWebView替换UIWebView

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