美文网首页iOS
iOS开发 WKWebView使用

iOS开发 WKWebView使用

作者: MrZhaoCn | 来源:发表于2017-01-17 21:59 被阅读595次

    自iOS8之后苹果推出了WKWebView,WKWebView相比于UIWebView性能更好,对于不需要支持iOS8的app,可以考虑将UIWebView切换到WKWebView.下面从四个方面介绍WKWebView的使用。
    1.加载网页,加载网页比较简单,以加载本地网页为例:

        WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
        webView.navigationDelegate = self;
        webView.UIDelegate = self;
        //加载本地网页
        NSString *path = [[NSBundle mainBundle] pathForResource:@"JSCallOC.html" ofType:nil];
        NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
        [webView loadFileURL:url allowingReadAccessToURL:url];
    
    

    2.webView加载的相关回调

    #pragma WKNavigatonDelegate
    // 1 页面开始加载
    - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
        
    }
    // 2 开始获取到网页内容时返回
    - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
        
    }
    //3 页面加载完成之后调用
    - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    }
    //4 页面加载失败之后调用
    - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
        
    }
    // 5接收到服务器跳转请求之后调用
    - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
        
    }
    // 6在收到响应后,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
        
        //    NSLog(@"%@",navigationResponse.response.URL.absoluteString);
        //    //允许跳转
        decisionHandler(WKNavigationResponsePolicyAllow);
        //不允许跳转
        //decisionHandler(WKNavigationResponsePolicyCancel);
    }
    // 7在发送请求之前,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
        
        //    NSLog(@"%@",navigationAction.request.URL.absoluteString);
        //    //允许跳转
        decisionHandler(WKNavigationActionPolicyAllow);
        //不允许跳转
        //decisionHandler(WKNavigationActionPolicyCancel);
    }
    #pragma 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();
    }
    

    3.WKWebView的进度条显示
    WKWebView自带一个属性estimatedProgress,我们可以通过监听它的改变来制作进度条,相比于UIWebView不带进度条要方便很多.

    @interface ViewController ()<WKNavigationDelegate,WKUIDelegate,WKScriptMessageHandler>
    @property (nonatomic, weak) WKWebView *webView;
    @property (nonatomic, weak) UIProgressView *progressBar;
    @end
    - (void)viewDidLoad {
        [super viewDidLoad];
        [self configWebView];
        [self configProgressBar];
    }
    - (void)configWebView {
        //配置环境
        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
        WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
        webView.navigationDelegate = self;
        webView.UIDelegate = self;
        //加载本地网页
        NSString *path = [[NSBundle mainBundle] pathForResource:@"JSCallOC.html" ofType:nil];
        NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
        [webView loadFileURL:url allowingReadAccessToURL:url];
        
        //webView的进度条
        [webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
        
        [self.view addSubview:webView];
        self.webView = webView;
    }
    - (void)configProgressBar {
        UIProgressView *progressgBar = [[UIProgressView alloc] init];
        progressgBar.frame = CGRectMake(0, 0, self.view.frame.size.width, 3);
        progressgBar.progress = 0.0;
        progressgBar.tintColor = [UIColor blueColor];
        [self.view addSubview:progressgBar];
        self.progressBar = progressgBar;
    }
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        if ([keyPath  isEqual: @"estimatedProgress"]) {
            self.progressBar.alpha = 1.0;
            [self.progressBar setProgress:self.webView.estimatedProgress animated:true];
            //进度条的值最大为1.0
            if (self.webView.estimatedProgress >= 1) {
                [UIView animateWithDuration:0.3 delay:0.1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
                    self.progressBar.alpha = 0.0;
                } completion:^(BOOL finished) {
                    self.progressBar.progress = 0;
                }];
            }
        }
    }
    - (void)dealloc{
        //这里需要注意,前面增加过的方法一定要remove掉。
        [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
    }
    
    

    4.WKWebView与js交互
    WKWebView与js的交互相比于UIWebView要简单一点,OC调用js方法只需要在- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;的回调方法里面做就好了,比如我们的h5页面有个全局的js方法叫ocCallJs(),该方法返回一个字符串。因为js代码调用是异步的,所以使用block做为回调就可以拿到js方法返回的数据,这一点UIWebView是做不到的。只需要这么调用

    - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
        [webView evaluateJavaScript:@"window.ocCallJs()" completionHandler:^(id _Nullable resualt, NSError * _Nullable error) {
            NSLog(resualt);
        }];
    }
    

    js调用OC在WKWebView中也是比较简单的,WKWebView提供了一个叫WKUserContentController类,我们只需要在创建WKWebView的时候,注册js需要回调的OC方法,然后初始化webView,遵循WKScriptMessageHandler,实现- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message ;方法即可,当js调用OC的方法时就会调用这个方法。比如我们的h5页面有个按钮,按钮点击回调OC的方法。
    h5页面的js方法需要这样写

    function call()
            {
                //前端需要用 window.webkit.messageHandlers.注册的方法名.postMessage({body:传输的数据} 来给native发送消息,jsCallOC就是我们开始注册的native函数,body里面传递我们想要的参数,如果是需要传递复杂数据,可以利用字典或者son格式传递。
                window.webkit.messageHandlers.jsCallOC.postMessage({body: 'hello world!'});
                alert("测试")
            }
    

    控制器里的完整代码这样写:

    #import "ViewController.h"
    #import <WebKit/WebKit.h>
    @interface ViewController ()<WKNavigationDelegate,WKUIDelegate,WKScriptMessageHandler>
    @property (nonatomic, weak) WKWebView *webView;
    @property (nonatomic, weak) UIProgressView *progressBar;
    @property (nonatomic, strong)WKUserContentController *userContentController;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        [self configWebView];
       [self configProgressBar];
    }
    - (void)configWebView {
        //配置环境
        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
        self.userContentController =[[WKUserContentController alloc] init];
        //注册方法
        [self.userContentController addScriptMessageHandler:self  name:@"jsCallOC"];//注册一个name为jsCallOC的js方法
        configuration.userContentController = self.userContentController;
        WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
        webView.navigationDelegate = self;
        webView.UIDelegate = self;
        //加载本地网页
        NSString *path = [[NSBundle mainBundle] pathForResource:@"JSCallOC.html" ofType:nil];
        NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
        [webView loadFileURL:url allowingReadAccessToURL:url];
        
        //webView的进度条
        [webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
        
        [self.view addSubview:webView];
        self.webView = webView;
    }
    - (void)configProgressBar {
        UIProgressView *progressgBar = [[UIProgressView alloc] init];
        progressgBar.frame = CGRectMake(0, 0, self.view.frame.size.width, 3);
        progressgBar.progress = 0.0;
        progressgBar.tintColor = [UIColor blueColor];
        [self.view addSubview:progressgBar];
        self.progressBar = progressgBar;
    }
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        if ([keyPath  isEqual: @"estimatedProgress"]) {
            self.progressBar.alpha = 1.0;
            [self.progressBar setProgress:self.webView.estimatedProgress animated:true];
            //进度条的值最大为1.0
            if (self.webView.estimatedProgress >= 1) {
                [UIView animateWithDuration:0.3 delay:0.1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
                    self.progressBar.alpha = 0.0;
                } completion:^(BOOL finished) {
                    self.progressBar.progress = 0;
                }];
            }
        }
    }
    - (void)dealloc{
        //这里需要注意,前面增加过的方法一定要remove掉。
        [self.userContentController removeScriptMessageHandlerForName:@"jsCallOC"];
        [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
    }
    #pragma WKNavigatonDelegate
    // 1 页面开始加载
    - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
        
    }
    // 2 开始获取到网页内容时返回
    - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
        
    }
    //3 页面加载完成之后调用
    - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
        [webView evaluateJavaScript:@"window.ocCallJs()" completionHandler:^(id _Nullable resualt, NSError * _Nullable error) {
            NSLog(resualt);
        }];
    }
    //4 页面加载失败之后调用
    - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
        
    }
    // 5接收到服务器跳转请求之后调用
    - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
        
    }
    // 6在收到响应后,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
        
        //    NSLog(@"%@",navigationResponse.response.URL.absoluteString);
        //    //允许跳转
        decisionHandler(WKNavigationResponsePolicyAllow);
        //不允许跳转
        //decisionHandler(WKNavigationResponsePolicyCancel);
    }
    // 7在发送请求之前,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
        
        //    NSLog(@"%@",navigationAction.request.URL.absoluteString);
        //    //允许跳转
        decisionHandler(WKNavigationActionPolicyAllow);
        //不允许跳转
        //decisionHandler(WKNavigationActionPolicyCancel);
    }
    #pragma 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();
    }
    #pragma WKWKScriptMessageHandler
    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
        NSLog(@"name:%@\\\\n body:%@\\\\n",message.name,message.body);
    }
    @end
    
    

    以上就是WKWebView的基本使用,demo地址:https://github.com/MrZhaoCn/WKWebView.git

    相关文章

      网友评论

      • 天津的树懒:请问用WKWebView控件来显示网页的话,会不会有缓存?如果有缓存的话怎样去除呢?
        MrZhaoCn:@树懒_2017 看你的缓存策略了,第一次request时可以设置仅从服务端取,但进入第二级的时候默认好像是有缓存的。iOS9后,wkwebView有清除缓存的api,你可以查下。又或者让后端配合,让header里面的Expires字段和Date字段一样,这样缓存每次都过期,就不会取缓存了。
      • 天津的树懒:Demo中项目运行以后,为什么每次都要等个两三秒钟的时间,才能加载本地的网页呢?
      • 天津的树懒:进度条不能显示。
        天津的树懒:@MrZhaoCn 看到进度条了,谢谢。
        MrZhaoCn:@树懒_2017 进度条是有的,在页面最顶上面,你注意看下
        天津的树懒:demo中的进度条不能显示。

      本文标题:iOS开发 WKWebView使用

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