WKWebView 封装

WKWebView 封装

随着H5的普及,APP中使用H5来实现的功能和页面也多了起来。要做图文混排布局的一般就采用H5来实现,而不是使用原生控件,这样处理起来比较方便。所以封装一个WKWebView来处理H5就比较有必要了。

封装时一般要处理的情况

  1. 登录的处理
  2. 返回的处理
  3. 进度条处理
  4. js交互

登录的处理

登录的处理我采用的是往request的header里面加token字段,H5判断是否有token来判断用户是否登录,具体实现是在decidePolicyForNavigationAction代理方法中先拦截,添加完token后reload一次。

1
2
3
4
5
6
7
8
9
10
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
if (!navigationAction.request.allHTTPHeaderFields[@"token"]) {
// 拦截
decisionHandler(WKNavigationActionPolicyCancel);
// 重新请求
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
[request setValue:[CODUserModel unarchive].token?:@"" forHTTPHeaderField:@"token"];
[self.wkWebView loadRequest:request];
}
}

返回的处理

返回主要是处理像百度新闻页这样的页面,我们先从百度首页点进百度新闻,然后多切换几次栏目后,点击返回按钮,直接执行[self.webView goBack]方法,发现是返回到上一个栏目,而不是返回到百度首页,这就需要我们进行处理。我的思路是采用一个数组来存放网页地址,先判断一下当前页面的title,一般切换栏目时,title是不变的,通过title来判断是否为同一页面,如果不是同一页面,则存放到数组里面。点击返回的时候,不执行[self.webView goBack]方法,而是重新load前面的网页。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
// 判断是否当前页
self.isCurrentPage = [webView.title isEqualToString:self.lastTitleString];
if (!self.isCurrentPage) {
[self.pages addObject:self.wkWebView.URL.absoluteString];
}
self.lastTitleString = webView.title;
}
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
// 判断是否当前页
self.isCurrentPage = [webView.title isEqualToString:self.lastTitleString];
if (!self.isCurrentPage) {
[self.pages addObject:self.wkWebView.URL.absoluteString];
}
self.lastTitleString = webView.title;
}
#pragma mark - backAction
- (void)backAction {
// 返回处理
if (self.pages.count > 1) {
[self.wkWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.pages[self.pages.count-2]]]];
[self.pages removeLastObject];
[self.pages removeLastObject];
}
else {
if (self.presentingViewController) {
// present一个navigation,push了页面
if (self.navigationController.viewControllers.count >= 1) {
[self dismissViewControllerAnimated:YES completion:nil];
}
else {
[self.navigationController popViewControllerAnimated:YES];
}
}
else {
[self.navigationController popViewControllerAnimated:YES];
}
}
}

进度条处理

进度条的处理就比较简单,主要使用KVO监听WKWebView的estimatedProgress属性变化,然后显示出来就行了。
viewDidLoad添加监听

1
2
// 添加进度条监听
[self.wkWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];

处理进度条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"estimatedProgress"]) {
if (self.wkWebView.estimatedProgress < 1.0) {
self.progressView.alpha = 1;
self.progressView.hidden = NO;
self.progressView.progress = self.wkWebView.estimatedProgress;
}
else {
[UIView animateWithDuration:0.3 animations:^{
self.progressView.alpha = 0;
} completion:^(BOOL finished) {
self.progressView.hidden = YES;
}];
}
}
}

js交互

原生与js交互的内容挺多的,详细的可以查看一下这篇WebView与JS的几种交互,我这里就简单的写一下
1. OC 调用JS

1
2
3
[webView evaluateJavaScript:@"document.getElementById('right_item').innerHTML" completionHandler:^(NSString *itemTitle, NSError * _Nullable error) {
NSLog(@"%@", itemTitle);
}];
  1. JS调用原生
    demo里面用的是URL拦截的方式来实现的。
1
2
3
4
5
6
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
decisionHandler(WKNavigationActionPolicyAllow);
// 处理js交互
[self jsActionWithUrlString:navigationAction.request.URL.absoluteString];
}

jsActionWithUrlString方法我放到了category里面,这样就不用挤到一个viewController里面了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (BOOL)jsActionWithUrlString:(NSString *)urlString {
NSLog(@"%@", urlString);
NSArray *urlArray = [urlString componentsSeparatedByString:@"://"];
//https 为你的URLScheme
if ([urlArray[0] isEqualToString:@"https"]) {
if (urlArray.count>1) {
// 判断路由,决定调用哪个方法
if ([urlArray[1] isEqualToString:@"m.baidu.com/"]) {
[self baidu];
}
}
}
return YES;
}
#pragma mark jsAction
- (void)baidu {
NSLog(@"this is a baidu site");
}

还有一些关于缓存,Cookie,跨域的访问等问题没有在项目中用到过,也就没有处理了,欢迎大家进行补充和指正。

demo下载:https://github.com/ljcoder2015/WKWebView