UIWebView的加載進(jìn)度條

導(dǎo)語

在一個(gè)陽光明媚的午后,組織終于決定把公司的SDK產(chǎn)品,由Native更換成H5,你沒看錯(cuò),就是用Native界面做的SDK,說多了都是眼淚。產(chǎn)品說,網(wǎng)頁加載的時(shí)候要有進(jìn)度條,OK,沒問題,一個(gè)字就是“干”,你懂的。

現(xiàn)在的iOS 應(yīng)用中,或多或少都會(huì)有H5頁,因?yàn)镠5有Native所不具備的靈活性,比如應(yīng)用中的活動(dòng)展示,需要不定時(shí)的更新,使用H5來做就能輕松搞定!iOS中常用的H5容器有兩種:1、 UIWebView 2、WKWebView。

  • WKWebViewiOS8.0以后開放的API,可以通過KVO來監(jiān)聽estimatedProgress屬性的變化來獲取當(dāng)前網(wǎng)頁的加載進(jìn)度,如果你的項(xiàng)目不用適配iOS6、iOS7(太爽了,幸福感爆棚有木有),那么你可以很easy的做一個(gè)WebView的進(jìn)度條。
  • UIWebView 如果你的應(yīng)用需要適配iOS7、或者iOS6 或者更早??。UIWebView是你不二的(也是唯一的)選擇,查看系統(tǒng)的API后并沒有發(fā)現(xiàn)可以獲取加載進(jìn)度的途徑,so...你看到的網(wǎng)頁有加載進(jìn)度,如果它是用UIWebView加載的,那進(jìn)度條一定是 假的! 假的!假的!??

應(yīng)商戶要求,SDK最低需要支持iOS6.0,然而組織并沒有6.0系統(tǒng)的測試機(jī),領(lǐng)導(dǎo)決定,把SDK最低支持的系統(tǒng)為設(shè)為6.0,雖然沒有真機(jī)試過,到底能不能在6.0系統(tǒng)上使用也未知,鑒于這些,H5只能通過UIWebView來加載了。

廢話不多說,先來個(gè)成品的效果圖:

效果1
圖片1
效果2
圖片2

Demo下載鏈接

1、第一次嘗試

進(jìn)度條嘛,第一反應(yīng)就是用系統(tǒng)的UIProgressView,然而試過之后才發(fā)現(xiàn)系統(tǒng)的UIProgressView限制太多,可以自定義的太少了,所以就放棄了。

2、第二次嘗試

百度、Google一番,好多道友都建議使用CAShapeLayer來做,好吧,干!
查看系統(tǒng)CAShapeLayer的API發(fā)現(xiàn),CAShapeLayer有兩個(gè)屬性

/* 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的設(shè)置這兩個(gè)描邊的起始和結(jié)束位置是有動(dòng)畫效果
自定一個(gè)類DKProgressLayer繼承自CAShapeLayer

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

@interface DKProgressLayer : CAShapeLayer

@property (nonatomic, strong) UIColor *progressColor;
/**
 進(jìn)度條開始加載
 */
- (void)progressAnimationStart;
/**
 進(jìn)度條加載完成
 */
- (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;
}

/* 不斷設(shè)置layer描邊的結(jié)束位置 */
- (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,拿給產(chǎn)品去看……

產(chǎn)品:這也太low了吧
猿:握草,微信的效果就是這樣好不好,絲般順滑。
產(chǎn)品:不行,太大眾化了,改成一邊加載一邊漸變的,前邊加載,后邊漸隱的
猿:尼瑪……那不就是QQ的加載效果么,那就不low了嗎?
產(chǎn)品:我就要那樣的效果,你就告訴我能不能做?
猿:你大爺……

3、第三次嘗試

繼續(xù)百度、Google,并沒有發(fā)現(xiàn)很好的思路,快要絕望的時(shí)候發(fā)現(xiàn)CAShapeLayer有一個(gè)子類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;

看注釋,貌似能設(shè)置起始點(diǎn)、終點(diǎn),還能設(shè)置多種顏色,還能設(shè)置顏色的位置,我湊,這不就能滿足產(chǎn)品??的需求了嗎? 快試試……此處省略過程,直接貼代碼了

- (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% 減緩進(jìn)度條增長速度 */
    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;
    }
}

效果如下:
圖片

產(chǎn)品:這么簡單的一個(gè)東西,你弄這么久,先這么著吧,有什么想法再找你
猿:我日尼瑪,傻吊……

好吧,這個(gè)需求就暫時(shí)告一段落了,產(chǎn)品需求來了再改吧??

本文github鏈接,如果你覺得能幫到你,路過給個(gè)Star哈。

參考鏈接:

http://www.itdecent.cn/p/b32b9fb6cb0a 感謝作者的思路

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,177評(píng)論 4 61
  • 畢業(yè)已經(jīng)快半年了?,F(xiàn)在在一家挺不錯(cuò)的公司上班,說實(shí)話,當(dāng)時(shí)沒想到自己能進(jìn)這家公司,因?yàn)楫吘刮乙粋€(gè)三流的本科學(xué)歷...
    停不住的夢想閱讀 434評(píng)論 0 0
  • 大家可以把你認(rèn)為這組圖講的是什么故事,寫在評(píng)論里,我們一起來講述我的的故事。
    今析何兮閱讀 178評(píng)論 1 0

友情鏈接更多精彩內(nèi)容