源码分享:仿微信的WebViewProgress

摘要

这是一个仿照微信的WebViewProgress,与github上多星的 NJKWebViewProgress 有所区别,有兴趣可以看看。

由来

iOS 与 Android 的区别

最近看到在项目里面的 webview 加载的时候使用菊花图转圈圈,由于iOS的 web 加载机制与 Android 不同:Android 是会等整个页面load完毕再进行显示;而 iOS 并非这样,会一边 load 一边显示。所以设计 iOS 和 Android 一样使用菊花图,反而浪费了 iOS 本身加载的优势。因为我们只能在: webViewDidFinishLoad 方法让菊花图消失,加载网页的过程,菊花图会一直挡住前面,影响我们浏览。

UIWebView 本身的限制

而熟悉iOS开发的的都知道,iOS 的 UIWebView 并没有向我们提供与加载网页进度相关的 API,我们是没有办法得知加载进度的。我们能使用的只有 UIWebViewDelegate 以下几个方法:

1
2
3
- (void)webViewDidStartLoad:(UIWebView *)webView;      //开始加载
- (void)webViewDidFinishLoad:(UIWebView *)webView //加载完毕
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error //加载失败

思路

NJKWebViewProgress 的实现

webViewDidStartLoad 是一个请求的开始,所有的请求都要经过它,未加载资源之前,能够得到一个URL 有多少个资源需要加载,使用_loadingCount++ 方式来计数。
webViewDidFinishLoaddidFailLoadWithError 是一个请求的结束,每次请求结束 _loadingCount –,并重新计数进度,进度使用 _loadingCount/_maxLoadCount 的方式来计算。但是这种做法也是有一定弊端的:进度条经常卡在1/4的地方,然后突然启动加载完毕,有点不流畅

我猜测的wechat的做法

我们在平时使用wechat时,会发现即使你网络很差,甚至是没有网络的时候,加载的进度条还是会一直走到达末端停止,最后超时才显示网络故障;这样无疑会给用户较好的体验,毕竟进度条一直走,即使网页没有在加载,我们也会以为他一直在加载,很安心的feel。

我的做法

我参考了wechat的做法,写了一个加速度的三次递减的进度条。

把进度条设置为 1000,初始化为100,各个阶段的递增值如下:

  • 0 ~ 500: 每 100ms 增加 50
  • 500 ~ 700: 每 100ms 增加 20
  • 700 ~ 800: 每 100ms 增加 15
  • 800 ~ 920: 每 100ms 增加 1
  • 920 :卡住等待超时

用到 GCD 的延时循环加载。具体可查看代码。

效果图

网络正常


网络不正常

使用

导入头文件

1
#import "LZProgresSHeader.h"

需要对象

1
2
LZWebViewProgressView *_progressView;
LZWebViewProgress *_progressProxy;

具体代码

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#import "ViewController.h"
#import "LZProgresSHeader.h"

@interface ViewController ()<UIWebViewDelegate,LZWebViewProgressDelegate>

@property (weak, nonatomic) IBOutlet UIWebView *webView;

@end

@implementation ViewController
{
LZWebViewProgressView *_progressView;
LZWebViewProgress *_progressProxy;
}

- (void)viewDidLoad
{
[super viewDidLoad];

self.automaticallyAdjustsScrollViewInsets = NO;

_progressProxy = [[LZWebViewProgress alloc] init];
_webView.delegate = _progressProxy;
_progressProxy.webViewProxyDelegate = self;
_progressProxy.progressDelegate = self;

CGFloat progressBarHeight = 2.f;
CGRect navigationBarBounds = self.navigationController.navigationBar.bounds;
CGRect barFrame = CGRectMake(0, navigationBarBounds.size.height - progressBarHeight, navigationBarBounds.size.width, progressBarHeight);
_progressView = [[LZWebViewProgressView alloc] initWithFrame:barFrame];
_progressView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;

NSURLRequest *req = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.bilibili.com"]];
[self.webView loadRequest:req];

}


- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];

}

- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController.navigationBar addSubview:_progressView];
}

-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[_progressView removeFromSuperview];
}

#pragma mark - NJKWebViewProgressDelegate
-(void)webViewProgress:(LZWebViewProgress *)webViewProgress updateProgress:(float)progress
{
[_progressView setProgress:progress];
}

@end

具体可查看我的 demo 实现。

源码地址

这里面的 LZWebViewProgress 文件是需要导入的SDK; WechatWebViewProgressDemo 是上面我写的使用演示demo。

github链接

最后的话

今天!剪了!最帅!的!短!头发!