终于到实战篇了,中间拖的有点久,主要是事情太多了,且这一篇有大量的抽象内容需要用文字来描述,真真是不好写啊,不过好歹是写完了,噢噢噢噢~
本篇的代码我进行了封装并放进了这个git仓库中,需要的同学自取。
https://github.com/DHUsesAll/DHProgressView
圆形渐变进度条
“那个,DH呀,你过来一下,这是我们的新需求,我们要做一个这样的效果出来。”产品经理把我叫了过去,并向我出示了美工sama刚完成的效果图,一个非常骚气的进度条效果,如图。
啥玩意儿啊,咋回事儿啊,这咋整啊,旁边安卓兄弟也是一脸吃了翔的表情。
以上场景很可能就是大家工作的时候会遇到的情况,那么,如果真的遇到了,如图的进度条效果,你们能实现吗?
在学习了实战篇和技巧篇的内容后,我相信你也有了一些实现思路了,这里似乎缺了最后一块拼图:渐变效果的实现。
CAGradientLayer
在iOS的绘图体系中,CALayer扮演了重要的角色。之前也提到过,系统为我们提供了大量的CALayer的子类供我们使用,如果我们想要实现颜色渐变的效果,那么我们可以使用其中一个子类:CAGradientLayer。
NOTE:事实上除了CAGradientLayer,你还可以使用CoreGraphics在上下文中绘制渐变效果,然后渲染到一个CALayer上面,但是其基于C语言的API比较繁琐,除非是非常复杂的、对性能要求较高的场景,否则还是推荐直接使用CoreAnimation的API来进行绘制。
FYI:使用CAGradientLayer只能实现线性渐变的效果,而CoreGraphics的绘制能绘制更多的渐变效果,大家如果感兴趣可以去查阅相关文档。
要知道CAGradientLayer是如何工作的,只需要打开PhotoShop,看看里面的“线性渐变工具”就行了。
CAGradientLayer的关键属性
- colors
colors属性表示了参与渐变的颜色,是一个CGColorRef数组,所以这里我们要使用桥接把CGColorRef转换为id类型放进NSArray中。
- startPoint、endPoint
startPoint和endPoint确定了一个向量,表示渐变的方向、渐变开始的地方和渐变结束的地方。就像你们在PS中使用线性渐变工具一样,需要拉一条线,表示了颜色插值的开始和结束。这里CAGradientLayer使用的是线性插值算法,如果你从左往右拉了一条线,则CAGradientLayer会从colors属性中的第一个颜色,从左往右地渐变为最后一个颜色,待会来看效果。要注意的是它们的x,y取值范围是[0,1],是一个相对值,(0,0)表示layer的左上角,(1,1)表示layer的右下角。
- locations
locations可以理解为参与渐变的颜色的终点值所占的比例,其count值需要和colors的count值一致(不一致也没关系,但是效果可能就和你想要的不一样了)。在PS中你可以在设置渐变色的时候拖动游标来设置每个颜色的location。
我们先令startPoint和endPoint所确定的向量为向量 a⃗ 。locations数组中的元素是整型NSNumber,表示每个颜色位于 a⃗ 上的位置,其取值范围是[0,1],0表示向量起点,1表示向量终点。默认的locations属性是nil,则每个颜色平均分配进度。举个例子:渐变色为红->黄->绿, a⃗ =(1,0) (向量的标准表达式,还记得吗),locations为@[@0,@0.3,@1],则表示把红色放在最左边,黄色放在距离红色0.3 * layer宽的位置,蓝色放在最右边,那么蓝色距离黄色就是(1-0.3) * layer宽。此时三个颜色把整个layer分成了三段,红色到黄色的这一段渐变占了整个layer左边起百分之三十的部分,剩下的百分之七十就是从黄色渐变为蓝色。
构造CAGradientLayer
我们来把上面的例子用代码实现出来看一看效果。
为了方便我们对颜色进行桥接,先来一个宏定义:
#define CGColorToNSObject(x) (__bridge id)x.CGColor
然后在ViewDidLoad中构造一个CAGradientLayer:
- (void)viewDidLoad {
[super viewDidLoad];
// 所有的layer都使用便利构造
CAGradientLayer * layer = [CAGradientLayer layer];
layer.frame = CGRectMake(100, 200, 250, 40);
// 渐变颜色为红->黄->绿
layer.colors = @[CGColorToNSObject([UIColor redColor]),CGColorToNSObject([UIColor yellowColor]), CGColorToNSObject([UIColor greenColor])];
// 起始点在左上角,结束点在右上角,则相当于从左往右画了一条线,所以确定了渐变方向是从左到右
// 这里随意修改它们两个的y值,只要保持一致,都能确定渐变方向为从左往右
layer.startPoint = CGPointMake(0, 0);
layer.endPoint = CGPointMake(1, 0);
// 设置locations
layer.locations = @[@0,@0.3,@1];
[self.view.layer addSublayer:layer];
}
效果:
你可以随意修改location和startPoint还有endPoint的值再运行看看,加深对这几个属性的理解。
回到主题
好了,我们的最后一块拼图终于有了,是时候开始拼图了。
我们要的圆形进度条效果似乎是把刚刚画的那个条形的渐变layer给掰弯了过来,这是我们直观上的第一反应,如果你朝这个方向思考,很可能你就会陷入想办法对这个条形layer去变形来达到效果,这显然是非常困难且不太好实现的。
我们的思路需要转变过来,实际上我们在实现动画效果的时候,需要把整个效果进行拆分,绘图也是一样的,不要第一眼看上去像什么就想当然朝着一个方向去思考。比如这个圆形进度条,大家要把”圆形“和”渐变“分开看。圆形的效果是如何实现的?渐变的效果是如何实现的?
思路
看到圆形,要绘制圆形的话第一个是不是应该想到CAShapeLayer?这时你脑海中应该会自动出现一个圆形的CAShapeLayer,那我们如何把这个shapeLayer填充成渐变的颜色呢?我们唯一所知的渐变的技术就是CAGradientLayer,显然我们最终需要使用一个CAGradientLayer来绘制渐变色,那这时你脑海中在刚才那个圆形的CAShapeLayer旁边又有了一坨CAGradientLayer,渐变色就是刚才的红->黄->绿。然后在脑海中,你把CAShapeLayer和CAGradientLayer慢慢的融合到一起,CAGradientLayer上面出现了一个CAShapeLayer圆环,我们要的圆形进度条效果,似乎就是把CAGradientLayer上面”抠“一个CAShapeLayer的形状下来,咦,这个效果好耳熟,似乎在哪里听说过。
蒙版!
gradientLayer.mask = shapeLayer;
That’s it !
想到这里,你兴奋不已,开始动起手来!然而你写到一半,似乎发现了一些问题。没关系,我们先实现第一个版本的效果来看看。
第一个坑
按照我们的思路,我们需要一个CAGradientLayer和一个圆形的CAShapeLayer。其中CAGradientLayer是一个正方形(其实无所谓,只要足够大,最终长什么样都是由它的蒙版来决定的,这里我们还是设定为正方形,且边长和CAShapeLayer的直径相等),CAShapeLayer需要设定一个半径和线宽(参考技巧篇第二篇文章讲解CA