UIWebView的加载进度条

导语

在一个阳光明媚的午后,组织终于决定把公司的SDK产品,由Native更换成H5,你没看错,就是用Native界面做的SDK,说多了都是眼泪。产品说,网页加载的时候要有进度条,OK,没问题,一个字就是“干”,你懂的。

现在的iOS 应用中,或多或少都会有H5页,因为H5有Native所不具备的灵活性,比如应用中的活动展示,需要不定时的更新,使用H5来做就能轻松搞定!iOS中常用的H5容器有两种:1、 UIWebView 2、WKWebView。

  • WKWebViewiOS8.0以后开放的API,可以通过KVO来监听estimatedProgress属性的变化来获取当前网页的加载进度,如果你的项目不用适配iOS6、iOS7(太爽了,幸福感爆棚有木有),那么你可以很easy的做一个WebView的进度条。
  • UIWebView 如果你的应用需要适配iOS7、或者iOS6 或者更早😂。UIWebView是你不二的(也是唯一的)选择,查看系统的API后并没有发现可以获取加载进度的途径,so...你看到的网页有加载进度,如果它是用UIWebView加载的,那进度条一定是 假的! 假的!假的!😁

应商户要求,SDK最低需要支持iOS6.0,然而组织并没有6.0系统的测试机,领导决定,把SDK最低支持的系统为设为6.0,虽然没有真机试过,到底能不能在6.0系统上使用也未知,鉴于这些,H5只能通过UIWebView来加载了。

废话不多说,先来个成品的效果图:

效果1
图片1
效果2
图片2

Demo下载链接

1、第一次尝试

进度条嘛,第一反应就是用系统的UIProgressView,然而试过之后才发现系统的UIProgressView限制太多,可以自定义的太少了,所以就放弃了。

2、第二次尝试

百度、Google一番,好多道友都建议使用CAShapeLayer来做,好吧,干!
查看系统CAShapeLayer的API发现,CAShapeLayer有两个属性

/* These values define the subregion of the path used to draw the
 * stroked outline. The values must be in the range [0,1] with zero
 * representing the start of the path and one the end. Values in
 * between zero and one are interpolated linearly along the path
 * length. strokeStart defaults to zero and strokeEnd to one. Both are
 * animatable. */
@property CGFloat strokeStart;
@property CGFloat strokeEnd;

意思就是CAShapeLayer的设置这两个描边的起始和结束位置是有动画效果
自定一个类DKProgressLayer继承自CAShapeLayer

#define DEVICE_WIDTH [UIScreen mainScreen].bounds.size.width

@interface DKProgressLayer : CAShapeLayer

@property (nonatomic, strong) UIColor *progressColor;
/**
 进度条开始加载
 */
- (void)progressAnimationStart;
/**
 进度条加载完成
 */
- (void)progressAnimationCompletion;

@end

@interface DKProgressLayer ()

@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, assign) CGFloat stepWidth;

@end

static NSTimeInterval const progressInterval = 0.01;

@implementation DKProgressLayer

- (instancetype)init {
    if (self = [super init]) {
        self.progressColor = [UIColor whiteColor];
        self.stepWidth = 0.01;
        self.lineWidth = 2;
        UIBezierPath *path = [UIBezierPath bezierPath];
        [path moveToPoint:CGPointMake(0, 2)];
        [path addLineToPoint:CGPointMake(DEVICE_WIDTH, 2)];
        self.path = path.CGPath;
        self.strokeEnd = 0;
    }
    return self;
}

- (void)setProgressColor:(UIColor *)progressColor {
    if (!progressColor) {
       return;
    }
    _progressColor = progressColor;
    self.progressColor = progressColor;
}

/* 不断设置layer描边的结束位置 */
- (void)progressChanged:(NSTimer *)timer {
    self.strokeEnd += _stepWidth;
      if (self.strokeEnd > 0.9) {
          _stepWidth = 0.0001;
      }
    }
}

- (void)progressAnimationStart {
    self.hidden = NO;
    if (_timer) {
        [self invalidateTimer];
    }
    _timer = [NSTimer scheduledTimerWithTimeInterval:progressInterval target:self selector:@selector(progressChanged:) userInfo:nil repeats:YES];
}

- (void)progressAnimationCompletion {
    [self invalidateTimer];
    self.strokeEnd = 1.0;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.hidden = YES;
        _stepWidth = 0.01;
        self.strokeEnd = 0;
    });
}

- (void)invalidateTimer {
    [_timer invalidate];
    _timer = nil;
}

@end

好吧,效果来了:
图片3

So easy too Happy,拿给产品去看……

产品:这也太low了吧
猿:握草,微信的效果就是这样好不好,丝般顺滑。
产品:不行,太大众化了,改成一边加载一边渐变的,前边加载,后边渐隐的
猿:尼玛……那不就是QQ的加载效果么,那就不low了吗?
产品:我就要那样的效果,你就告诉我能不能做?
猿:你大爷……

3、第三次尝试

继续百度、Google,并没有发现很好的思路,快要绝望的时候发现CAShapeLayer有一个子类CAGradientLayer

/* The array of CGColorRef objects defining the color of each gradient
 * stop. Defaults to nil. Animatable. */

@property(nullable, copy) NSArray *colors;

/* An optional array of NSNumber objects defining the location of each
 * gradient stop as a value in the range [0,1]. The values must be
 * monotonically increasing. If a nil array is given, the stops are
 * assumed to spread uniformly across the [0,1] range. When rendered,
 * the colors are mapped to the output colorspace before being
 * interpolated. Defaults to nil. Animatable. */

@property(nullable, copy) NSArray<NSNumber *> *locations;

/* The start and end points of the gradient when drawn into the layer's
 * coordinate space. The start point corresponds to the first gradient
 * stop, the end point to the last gradient stop. Both points are
 * defined in a unit coordinate space that is then mapped to the
 * layer's bounds rectangle when drawn. (I.e. [0,0] is the bottom-left
 * corner of the layer, [1,1] is the top-right corner.) The default values
 * are [.5,0] and [.5,1] respectively. Both are animatable. */

@property CGPoint startPoint;
@property CGPoint endPoint;

看注释,貌似能设置起始点、终点,还能设置多种颜色,还能设置颜色的位置,我凑,这不就能满足产品🐶的需求了吗? 快试试……此处省略过程,直接贴代码了

- (void)setProgressStyle:(DKProgressStyle)progressStyle {
    _progressStyle = progressStyle;
    if (progressStyle == DKProgressStyle_Gradual) {
        self.strokeColor = nil;
        CAGradientLayer *gradientLayer = [CAGradientLayer layer];
        CGFloat RGB[3];
        [self getRGBComponents:RGB forColor:_progressColor];
        gradientLayer.colors = @[(__bridge id)[UIColor colorWithRed:RGB[0] green:RGB[1] blue:RGB[2] alpha:0.2].CGColor, (__bridge id)_progressColor.CGColor];
        gradientLayer.locations = @[@(0), @(0)];
        gradientLayer.startPoint = CGPointMake(0, 0);
        gradientLayer.endPoint = CGPointMake(1.0, 0);
        gradientLayer.frame = CGRectMake(0, 0, 0, 2);
        _gradientLayer = gradientLayer;
        [self addSublayer:gradientLayer];
    }
}
- (void)progressChanged:(NSTimer *)timer {
    self.strokeEnd += _stepWidth;
    /* 超过90% 减缓进度条增长速度 */
    if (self.strokeEnd > 0.9) {
        _stepWidth = 0.0001;
    }
    if (_progressStyle == DKProgressStyle_Gradual) {
        /* 不断改变layer颜色的起始位置 */
        _gradientLayer.locations = @[@(self.strokeEnd/2), @(self.strokeEnd)];
        /* 不断改变layer的frame */
        _gradientLayer.frame = CGRectMake(0, 0, DEVICE_WIDTH*self.strokeEnd, 2);
    }
}

/* 获取颜色的RGB值 */
- (void)getRGBComponents:(CGFloat [3])components forColor:(UIColor *)color {
    if (!color) {
        components[0] = 1;
        components[1] = 1;
        components[2] = 1;
        return;
    }
    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char resultingPixel[4];
    CGContextRef context = CGBitmapContextCreate(&resultingPixel, 1, 1, 8, 4, rgbColorSpace, kCGImageAlphaNoneSkipLast);
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
    CGContextRelease(context);
    CGColorSpaceRelease(rgbColorSpace);
    for (int component = 0; component < 3; component++) {
        components[component] = resultingPixel[component] / 255.0f;
    }
}

效果如下:
图片

产品:这么简单的一个东西,你弄这么久,先这么着吧,有什么想法再找你
猿:我日尼玛,傻吊……

好吧,这个需求就暂时告一段落了,产品需求来了再改吧😂

本文github链接,如果你觉得能帮到你,路过给个Star哈。

参考链接:

http://www.jianshu.com/p/b32b9fb6cb0a 感谢作者的思路

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容