導(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。
-
WKWebView 是iOS8.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

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
好吧,效果來了:
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哈。