UIWebView的加载进度条

作者: 夜满西楼 | 来源:发表于2017-03-17 13:48 被阅读31069次

    导语

    在一个阳光明媚的午后,组织终于决定把公司的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 感谢作者的思路

    相关文章

      网友评论

      • 奔跑的小菜菜:- (void)setProgressColor:(UIColor *)progressColor {
        if (!progressColor) {
        return;
        }
        _progressColor = progressColor;
        self.progressColor = progressColor;
        }
        老铁 确定这样不会死循环 哈哈
      • destiny_12:要是能封装成一个控件就好,最近在找一个webView的好控件,就是找不到。。。希望能给个建议。
      • deepindo:这个layer的高度怎么改,frame里面设置高好像不生效
      • Cass__:与产品的交流对话莫名的喜感:joy:
      • larryzhao:哇,好赞
      • 杨媛媛捷软世纪:写的不错,谢谢分享😊
      • 1594664e0327:正好用在项目里面了
      • 小太阳_0a5e:谢谢分享,很有用
      • 54a89b0a7d77:谢谢分享
      • catcherdream:猿:我日尼玛,傻吊…… 我无耻的笑了
        夜满西楼:哈哈,程序员跟产品之间的沟通就是一部血泪史:smiley:
      • catcherdream:抢个沙发。
      • jbtm:66666,一直想找这个效果
        夜满西楼:@nealHong :smile:

      本文标题:UIWebView的加载进度条

      本文链接:https://www.haomeiwen.com/subject/qidjnttx.html