UIWebView的加载进度条

导语

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

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

  • WKWebView 是iOS8.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 感谢作者的思路



作者:夜满西楼
链接:https://www.jianshu.com/p/aab5d7b962f4
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值