WKWebView使用及加载进度条实现

iOS8之前加载网页都是使用<UIKit/UIKit.h>中的UIWebView,iOS8新出了一个加载网页的控件[WKWebView],它属于<WebKit/WebKit.h>,使用WKWebView相比于UIWebView益处多多,这个具体可以自行百度。从去年开始很多应用都已从iOS8开始适配,如今更不用担心这个问题了。

其他的废话不需要多说

    1. 首先,Demo中新建一个控制器作为其他控制器的父类,并且公开一个返回上一控制器的方法。

BaseViewController:

#import <UIKit/UIKit.h>
#import "Masonry/Masonry.h"

@interface BaseViewController : UIViewController

- (void)zjsLeftBarButtonItemAction;

@end

#import "BaseViewController.h"

@interface BaseViewController ()

@end

@implementation BaseViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    self.navigationController.navigationBar.barTintColor = [UIColor orangeColor];
    self.navigationController.navigationBar.translucent = NO;
    
    ///右滑返回
    self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;
    
    [self zjsLeftBarButtonItem];
    
}

#pragma mark -左侧返回按钮
- (void)zjsLeftBarButtonItem
{
    if (self.navigationController.viewControllers.count > 1) {
        UIButton *backButton= [UIButton buttonWithType:UIButtonTypeCustom];
        backButton.frame = CGRectMake(0, 0, 64, 44);
        [backButton setImage:[UIImage imageNamed:@"navLeft"] forState:UIControlStateNormal];
        [backButton setImage:[UIImage imageNamed:@"navLeft"] forState:UIControlStateHighlighted];
        backButton.imageEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 50);
        [backButton addTarget:self action:@selector(zjsLeftBarButtonItemAction) forControlEvents:UIControlEventTouchUpInside];
        self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    }
}

- (void)zjsLeftBarButtonItemAction
{
    [self.navigationController popViewControllerAnimated:YES];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

复制代码

对于BaseViewController, 导航栏给了颜色且设置为不透明

MainStoryBoard直接添加的导航栏,右滑返回给干掉了,这里添加一下:

///右滑返回
    self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;
复制代码

重写返回按钮,并且公开了返回按钮响应事件 -> 这里对加载网页的控制器有作用,我的处理方式是如此的

- (void)zjsLeftBarButtonItemAction
{
    [self.navigationController popViewControllerAnimated:YES];
}
复制代码
    1. 默认的ViewController ,直接从SB拖出来一个按钮 添加点击事件 -> push到加载网页控制器
- (IBAction)pushWebViewAction:(id)sender {
    
    WebViewController *vc = [[WebViewController alloc] init];
    [self.navigationController pushViewController:vc animated:YES];
    
}
复制代码
    1. 使用WKWebView,需要引入<WebKit/WebKit.h>

WKWebView比较常用的两个代理<WKUIDelegate, WKNavigationDelegate>

采用懒加载方式创建WKWebView和进度条对象:

@property (nonatomic, strong) WKWebView *webView;
@property (nonatomic, strong) UIProgressView *progressView;

- (WKWebView *)webView
{
    if (!_webView) {
        _webView = [[WKWebView alloc] initWithFrame:Screen_Bounds];
        _webView.backgroundColor = [UIColor whiteColor];
        _webView.UIDelegate = self;
        _webView.navigationDelegate = self;
        _webView.allowsBackForwardNavigationGestures = YES;//允许右滑手势返回上一页面(网页)
    }
    return _webView;
}

- (UIProgressView *)progressView
{
    if (!_progressView) {
        _progressView = [[UIProgressView alloc] init];
        _progressView.backgroundColor = [UIColor clearColor];//背景色-若trackTintColor为clearColor,则显示背景颜色
        _progressView.progressTintColor = [UIColor blueColor];//进度条颜色
        _progressView.trackTintColor = [UIColor clearColor];//进度条还未到达的线条颜色
        
        _progressView.hidden = YES;//初始隐藏
    }
    return _progressView;
}

复制代码

ps: 这里,webView我允许了右滑返回网页手势

WebViewController:

#import "WebViewController.h"
#import <WebKit/WebKit.h>

#define Screen_Bounds [UIScreen mainScreen].bounds

@interface WebViewController ()<WKUIDelegate, WKNavigationDelegate>

@property (nonatomic, strong) WKWebView *webView;
@property (nonatomic, strong) UIProgressView *progressView;

@end

@implementation WebViewController

#pragma mark -lazy load
- (WKWebView *)webView
{
    if (!_webView) {
        _webView = [[WKWebView alloc] initWithFrame:Screen_Bounds];
        _webView.backgroundColor = [UIColor whiteColor];
        _webView.UIDelegate = self;
        _webView.navigationDelegate = self;
        _webView.allowsBackForwardNavigationGestures = YES;//允许右滑手势返回上一页面(网页)
    }
    return _webView;
}

- (UIProgressView *)progressView
{
    if (!_progressView) {
        _progressView = [[UIProgressView alloc] init];
        _progressView.backgroundColor = [UIColor clearColor];//背景色-若trackTintColor为clearColor,则显示背景颜色
        _progressView.progressTintColor = [UIColor blueColor];//进度条颜色
        _progressView.trackTintColor = [UIColor clearColor];//进度条还未到达的线条颜色
        
        _progressView.hidden = YES;//初始隐藏
    }
    return _progressView;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.title = @"WKWebView";
    
    [self testWKWebView];
    
}

#pragma mark -父类方法
- (void)zjsLeftBarButtonItemAction
{
    NSLog(@"--父类返回按钮响应事件");
    if ([self.webView canGoBack]) {
        [self canGoBackWebViewPage];
        [self rewriteLeftBarButton:YES];
    } else {
        [self popToLastViewController];
    }
}

#pragma mark -跳转网页
- (void)canGoBackWebViewPage
{
    [self.webView goBack];
}

#pragma mark -重写左侧返回按钮 是否添加关闭按钮
- (void)rewriteLeftBarButton:(BOOL)add
{
    UIButton *backButton= [UIButton buttonWithType:UIButtonTypeCustom];
    backButton.frame = CGRectMake(0, 0, 64, 44);
    [backButton setImage:[UIImage imageNamed:@"navLeft"] forState:UIControlStateNormal];
    [backButton setImage:[UIImage imageNamed:@"navLeft"] forState:UIControlStateHighlighted];
    backButton.imageEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 50);
    [backButton addTarget:self action:@selector(zjsLeftBarButtonItemAction) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    if (add) {
        UIButton *closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
        closeButton.frame = CGRectMake(0, 0, 44, 44);
        closeButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
        [closeButton setImage:[UIImage imageNamed:@"close"] forState:UIControlStateNormal];
        [closeButton addTarget:self action:@selector(popToLastViewController) forControlEvents:UIControlEventTouchUpInside];
        UIBarButtonItem *closeItem = [[UIBarButtonItem alloc]initWithCustomView:closeButton];
        UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
        //正: 向右 负: 向左
        space.width = -10;
        
        NSArray *array = [NSArray arrayWithObjects:backItem, space, closeItem, nil];
        self.navigationItem.leftBarButtonItems = array;
    } else {
        self.navigationItem.leftBarButtonItem = backItem;
    }
}

#pragma mark -pop
- (void)popToLastViewController
{
    [self.navigationController popViewControllerAnimated:YES];
}

#pragma mark -WKWebView
- (void)testWKWebView
{
    [self.view addSubview:self.webView];
    
    NSString *url = @"https://www.baidu.com";
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
    [self.webView loadRequest:request];
    
    [self.view addSubview:self.progressView];
    [self.progressView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.leading.and.trailing.equalTo(@0);
        make.top.equalTo(self.mas_topLayoutGuideBottom);
        make.height.equalTo(@2);
    }];
    
    ///KVO-title
    [self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
    
    ///KVO-estimatedProgress
    [self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:NULL];
}

/**
 KVO获取网页title
 
 @param keyPath 路径
 @param object 对象
 @param change 改变
 @param context 上下文
 */
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"title"]) {
        if ([object isEqual:self.webView]) {
            if (self.navigationController) {
                self.title = self.webView.title;
            }
        }
    } else if ([keyPath isEqualToString:@"estimatedProgress"]) {
        if ([object isEqual:self.webView]) {
            NSLog(@"-change: %@ ---estimatedProgress: %f", change, self.webView.estimatedProgress);
            [self.progressView setProgress:self.webView.estimatedProgress animated:YES];

            if (self.webView.estimatedProgress > 0 && self.webView.estimatedProgress < 1) {
                _progressView.hidden = NO;
            } else if (self.webView.estimatedProgress == 1) {

                __weak typeof(self) weakSelf = self;
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    weakSelf.progressView.hidden = YES;
                    [weakSelf.progressView setProgress:0 animated:NO];
                });
            }
        }
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

#pragma mark -WKNavigationDelegate
///发送请求之前 决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    NSLog(@"--发送请求之前 决定是否跳转-decidePolicyForNavigationAction");
    NSLog(@"%@",navigationAction.request.URL.absoluteString);
    
    //允许跳转
    decisionHandler(WKNavigationActionPolicyAllow);
    //不允许跳转
    //decisionHandler(WKNavigationActionPolicyCancel);
}

///页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
{
    NSLog(@"--页面开始加载调用-didStartProvisionalNavigation");
}

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
{
    NSLog(@"--接收到响应后,决定是否跳转--decidePolicyForNavigationResponse");
    NSLog(@"%@",navigationResponse.response.URL.absoluteString);
    
    //比如遇到某些字段 返回 -百度 南京天气预报
    NSString *backUrl = @"mipcache.bdstatic.com";
    if ([navigationResponse.response.URL.absoluteString rangeOfString:backUrl].location != NSNotFound) {
        NSLog(@"--这个字符串中包含: %@", backUrl);
        //回调的URL中如果含有backUrl,就直接返回,也就是关闭了webView界面
        [self.navigationController  popViewControllerAnimated:YES];
    }
    
    
    //允许跳转
    decisionHandler(WKNavigationResponsePolicyAllow);
    //不允许跳转
    //decisionHandler(WKNavigationResponsePolicyCancel);
}

- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation
{
    NSLog(@"--当内容开始返回时调用-didCommitNavigation");
    
}

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
    NSLog(@"--页面加载完成之后调用-didFinishNavigation");
    
}

- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation
{
    NSLog(@"--接收到服务器跳转请求之后-didReceiveServerRedirectForProvisionalNavigation");
    
}

- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
    NSLog(@"--页面加载失败调用-didFailProvisionalNavigation");

}

#pragma mark -WKUIDelegate
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
    NSLog(@"--创建一个新的webView-createWebViewWithConfiguration");
    return [[WKWebView alloc] init];
}

// 输入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler
{
    NSLog(@"%@---%@",prompt, defaultText);
    completionHandler(@"http");
    
}
// 确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler
{
    NSLog(@"%@",message);
    completionHandler(YES);
    
}
// 警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
    NSLog(@"%@",message);
    completionHandler();
}

- (void)dealloc
{
    [self.webView removeObserver:self forKeyPath:@"title"];
    [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
}
复制代码

对于获取网页标题和网页加载进度,采用的KVO方式监听到值的改变然后修改相应UI。 为什么采用KVO方式监听,这个IDE中有说明:

比如标题:

鼠标左键点到WKWebView,按住command + 鼠标左键会弹出如下图内容,然后左键点击Jump to Definition找到title 有如下说明.

/*! @abstract The page title.
 @discussion @link WKWebView @/link is key-value observing (KVO) compliant
 for this property.
 */
@property (nullable, nonatomic, readonly, copy) NSString *title;
复制代码

加载进度等变化采用KVO方式IDE中也有明确提到.

一定要记得在dealloc中移除监听,否则返回销毁控制器时会导致程序崩溃。

进度条的显示与隐藏我是通过判断KVO方式监听estimatedProgress的值来隐藏或显示的,有些iOSer通过webView的开始加载、成功或失败等回调去显示或隐藏,我觉得也没毛病。

以前公司维护的一个超级老的应用里面有大量的原生与JS等互调情况,不过使用的是UIWebView和<JavaScriptCore/JavaScriptCore.h>,关于这些网上可以搜到很多相关文章介绍。 至于使用WKWebView实现原生与JS的互调等 或许后边会研究下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值