文章結(jié)構(gòu)
- layer單位坐標
- 屬性講解
- 使用范例及代碼講解
注:網(wǎng)上有很多相關(guān)資料,但是總感覺代碼沒有注釋得很清楚,所以自己寫一篇梳理下,下面的例子網(wǎng)上也有,我只是按照自己的想法記錄下來,并注釋了自己的一些想法,有看過別人的例子但是沒有copy別人的代碼,能優(yōu)化的就優(yōu)化。
layer單位坐標
單位坐標和像素沒有關(guān)系,在你定義的layer中,x軸和y軸長度都是單位長度1,而且原點坐標為左上角,如下圖所示:

屬性講解
- colors
漸變的顏色,只設(shè)置一個顏色不起作用。 - locations
漸變顏色的分界線,文檔里說明每個元素的值在0到1之間,但是設(shè)置大于1或者小于1的數(shù)一樣起作用,下面代碼范例中有使用到。如果不設(shè)置則按照系統(tǒng)默認劃分;如果設(shè)置的元素個數(shù)比顏色個數(shù)少,則默認最后一條分界線處為1??创a是最好理解的,如下。
- 示例代碼:設(shè)置的分界線數(shù)和顏色個數(shù)一樣
- (void)configureLayerForMyView{
_gradientLayer = [CAGradientLayer layer];
_gradientLayer.colors = @[(id)[UIColor clearColor].CGColor,(id)[UIColor yellowColor].CGColor/*, (id)[UIColor redColor].CGColor*/];
_gradientLayer.locations = @[@(0.4 ), @(0.5)];
_gradientLayer.startPoint = CGPointMake(0, 0.0);//0
_gradientLayer.endPoint = CGPointMake(0, 1);//0.7
[_myVIew.layer addSublayer:_gradientLayer];
}
效果

天藍色為控件的背景色,從漸變方向上layer高度為0.4處開始漸變,0.5處結(jié)束漸變。
- 示例代碼:設(shè)置的分界線數(shù)比顏色個數(shù)少
- (void)configureLayerForMyView{
_gradientLayer = [CAGradientLayer layer];
_gradientLayer.colors = @[(id)[UIColor clearColor].CGColor,(id)[UIColor yellowColor].CGColor, (id)[UIColor redColor].CGColor];
_gradientLayer.locations = @[@(0.4), @(0.5)];//和設(shè)置@[@(0.4), @(0.5),@(1)]一樣
_gradientLayer.startPoint = CGPointMake(0, 0.0);//0
_gradientLayer.endPoint = CGPointMake(0, 1);//0.7
[_myVIew.layer addSublayer:_gradientLayer];
}
效果

最后的顏色分割線是在控件底部,也就是layer單位坐標的y軸為1處。
- startPoint
- endPoint
startPoint和endPoint這兩個屬性分別表示漸變的開始點和結(jié)束點。注意比如你設(shè)置的開始點為(0,0),結(jié)束點為(0,1),并不代表從(0,0)處開始漸變,開始漸變的位置還需要結(jié)合locations的設(shè)置來確定。個人使用習(xí)慣,我只是使用這兩個屬性來定義漸變的方向,比如沿x軸漸變開始點(0,0)結(jié)束點(1,0),沿y軸漸變開始點(0,0)結(jié)束點(0,1)。如果開始點不是在x軸或者y軸上,結(jié)束點也 不在x軸或者y軸上,通過設(shè)置locations屬性則得不到你想要的結(jié)果。
比如下代碼:
- (void)configureLayerForMyView{
_gradientLayer = [CAGradientLayer layer];
_gradientLayer.colors = @[(id)[UIColor clearColor].CGColor,(id)[UIColor yellowColor].CGColor/*, (id)[UIColor redColor].CGColor*/];
_gradientLayer.locations = @[@(0.4 ), @(0.5)];
_gradientLayer.startPoint = CGPointMake(0, 0.0);//0
_gradientLayer.endPoint = CGPointMake(0, 0.7);//0.7
[_myVIew.layer addSublayer:_gradientLayer];
}
效果

漸變的顏色分割線不在0.4和0.5處。
所以,就我個人的使用習(xí)慣,除非要改變顏色漸變的方向,否則這兩個屬性我一般不會去改變。這是個人見解,如有高見或者有誤的地方歡迎交流
使用范例及代碼講解
- 示例代碼:給圖片添加漸變顏色
- (void)configureGradientToImageview{
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = _beautyView.bounds;
gradientLayer.colors = @[(id)[UIColor blackColor].CGColor, (id)[UIColor clearColor].CGColor];
//the locations's value is between 0 and 1 according to the document, but it can work when it's nagative. I don't know why.
gradientLayer.locations = @[@(-1), @(1)];
[_beautyView.layer addSublayer:gradientLayer];
}
效果

注意這里的locations開始點設(shè)置為-1處,是為了讓圖片上的漸變顏色淺一些。可以把設(shè)置locations的那行代碼注釋掉試下效果,在文章結(jié)尾會有demo下載地址。
- 示例代碼:進度條
通過設(shè)置CALayer的mask屬性來實現(xiàn),必須要有兩個layer,一個是被遮罩層,一個是遮罩層,巧用遮罩的只顯示重疊部分來達到類似進度條效果,以后會另寫一篇文章來說明mask的使用。
- (void)configureGradientToFirstBottomView{
_gradientLayerForFirstBtmView = [CAGradientLayer layer];
[self setupLayer:_gradientLayerForFirstBtmView];
[_firstBottomView.layer addSublayer:_gradientLayerForFirstBtmView];
_maskLayer = [CALayer layer];
_maskLayer.frame = CGRectMake(0, 0, 0, _firstBottomView.bounds.size.height);
//configure color except for clearColor
_maskLayer.backgroundColor = [UIColor blueColor].CGColor;
_gradientLayerForFirstBtmView.mask = _maskLayer;
//configure timer to update maskLayer's frame
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateMask:) userInfo:nil repeats:YES];
}
- ( void)updateMask:(NSTimer *)timer{
//reset progress
if (_progress > 1) {
_progress = 0;
return;
}
//change the mask layer's frame
CGRect frame = _firstBottomView.bounds;
frame.size.width = _progress * _firstBottomView.bounds.size.width;
_maskLayer.frame = frame;
_progress += 0.01;
}
- (void)setupLayer:(CAGradientLayer *)layer{
layer.startPoint = CGPointMake(0, 0.5);
layer.endPoint = CGPointMake(1, 0.5);
NSMutableArray *arr = [NSMutableArray array];
for (NSInteger i = 0; i < 400; i+=5) {
UIColor *color = [UIColor colorWithHue:1.0 * i / 400 saturation:1 brightness:1 alpha:1];
[arr addObject:(id)color.CGColor];
}
layer.colors = [arr copy];
}
效果如下圖:

還有一種進度條樣式,就是先整體顯示整條進度,然后通過動態(tài)改變顏色來達到提示進度的效果,代碼如下:
- ( void)configureGradientLayerToSecondBottomView{
_gradientLayerForSecondBtmView = [CAGradientLayer layer];
[self setupLayer:_gradientLayerForSecondBtmView];
[_secondBottomView.layer addSublayer:_gradientLayerForSecondBtmView];
//configure timer to update the gradient layer's colors
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateColor:) userInfo:nil repeats:YES];
}
- (void)updateColor:(NSTimer *)timer{
[self updateColorForLayer:_gradientLayerForSecondBtmView];
}
- (void)updateColorForLayer:(CAGradientLayer *)layer{
NSMutableArray *originArr = [_gradientLayerForSecondBtmView.colors mutableCopy];
//retain the last object
id lastColor = originArr.lastObject;
//remove the last object in the arr
[originArr removeLastObject];
//insert the last object at the first
[originArr insertObject:lastColor atIndex:0];
NSArray *updateArr = originArr.copy;
_gradientLayerForSecondBtmView.colors = updateArr;
//configure animation for the colors property
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"colors"];
animation.toValue = updateArr;
animation.duration = 0.1;
[_gradientLayerForSecondBtmView addAnimation:animation forKey:nil];
}
效果如下圖:

-
示例代碼:在圓形軌跡上動態(tài)顯示運動點
實現(xiàn)思路:先在你自定義的控件中添加一個漸變圖層,然后給漸變圖層添加遮罩圖層,給遮罩圖層指定軌跡(比如圓形),就達到只展示一個圓了,最后一步動態(tài)顯示運動點,其實就是通過定時器來改變漸變圖層的locations屬性來達到的。
為了便于理解我們先看動態(tài)改變一個漸變圖層locations屬性的動畫效果,如下圖:
再在這個基層上給漸變圖層添加一個路徑為圓形的CAShaperLayer遮罩,這樣就可以達到我們的目的
代碼如下:
- (void)configureLayerForCircleView{
//1. add the gradient layer to circleview's layer
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = _circleView.bounds;
gradientLayer.colors = @[(id)[UIColor redColor].CGColor, (id)[UIColor purpleColor].CGColor, (id)[UIColor redColor].CGColor];
gradientLayer.startPoint = CGPointMake(0, 0.5);
gradientLayer.endPoint = CGPointMake(1, 0.5);
//u should init a location here or the animation will not be made.
gradientLayer.locations = @[@(-0.11), @(-0.1), @(0)];
[_circleView.layer addSublayer:gradientLayer];
//2. add a mask layer to the gradient layer
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
//prepare a path for the shapeLayer
CGPoint circleCenter = CGPointMake(_circleView.bounds.size.width / 2, _circleView.bounds.size.height / 2);
NSInteger radius = ceil( _circleView.bounds.size.height) / 2 - 5;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:circleCenter radius:radius startAngle:0 endAngle:M_PI *2 clockwise:YES];
shapeLayer.path = path.CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor redColor].CGColor;
shapeLayer.lineWidth = 5.0;
gradientLayer.mask = shapeLayer;
//3. set up a timer to change gradient layer's locations property
[NSTimer scheduledTimerWithTimeInterval:circleDuarationTime repeats:YES block:^(NSTimer * _Nonnull timer) {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"locations"];
animation.fromValue = @[@(-0.11), @(-0.1), @(0)];
animation.toValue = @[@(1), @(1.1), @(1.2)];
animation.duration = circleDuarationTime;
[gradientLayer addAnimation:animation forKey:nil];
}];
}
最終效果如下圖

- 示例代碼:兩張圖片大小一樣且在同一位置,先左后右動態(tài)顯示被遮住的另外一張圖。
實現(xiàn)思路:給另外一張圖的layer添加一個背景色為clearColor的mask圖層,這樣就把添加了mask圖層的圖片隱藏掉了(上面有說過添加mask圖層后只顯示重疊部分,因為這里的mask圖層是clearColor的,即alpha為0,這樣兩個圖層間就沒有重疊部分了,達到隱藏被添加了mask的控件目的),然后再在mask圖層上添加左、右兩個CAGradientLayer子圖層,漸變顏色由clearColor到其他顏色,且clearClolor部分要覆蓋掉圖片,寬度各為圖片控件的一半,高度一定要比圖片控件高。然后我們再動態(tài)改變這兩個圖層的position讓漸變色不為clearColor的部分覆蓋掉圖片控件,這樣被隱藏的圖片又暴露出來了。
代碼如下:
- (void)configureLayerForFrontImageview{
//1. add a mask to front image view
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = _frontImageview.bounds;
maskLayer.backgroundColor = [UIColor clearColor].CGColor;
_frontImageview.layer.mask = maskLayer;
//add the left side layer to the mask layer
_leftGradientLayer = [CAGradientLayer layer];
_leftGradientLayer.frame = CGRectMake(0, 0, _frontImageview.bounds.size.width / 2, _frontImageview.bounds.size.height * 3);
_leftGradientLayer.colors = @[(id)[UIColor clearColor].CGColor, (id)[UIColor blackColor].CGColor];
_leftGradientLayer.locations = @[@(0.4), @(0.5)];
[maskLayer addSublayer:_leftGradientLayer];
//add the right side layer to the mask layer
_rightGradientLayer = [CAGradientLayer layer];
_rightGradientLayer.frame = CGRectMake(_frontImageview.bounds.size.width / 2, 0, _frontImageview.bounds.size.width / 2, _frontImageview.bounds.size.height * 3);
_rightGradientLayer.colors = @[(id)[UIColor clearColor].CGColor, (id)[UIColor blackColor].CGColor];
_rightGradientLayer.locations = @[@(0.4), @(0.5)];
[maskLayer addSublayer:_rightGradientLayer];
//animation to show bg image view
[self showBgImage];
}
- (void)showBgImage{
[NSTimer scheduledTimerWithTimeInterval:circleDuarationTime repeats:NO block:^(NSTimer * _Nonnull timer) {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.toValue = [NSValue valueWithCGPoint:CGPointMake(_leftGradientLayer.position.x, _leftGradientLayer.position.y - _leftGradientLayer.bounds.size.height * 0.5)];
animation.duration = circleDuarationTime;
//keep the animation last state
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
[_leftGradientLayer addAnimation:animation forKey:nil];
}];
[NSTimer scheduledTimerWithTimeInterval:circleDuarationTime + 2 repeats:NO block:^(NSTimer * _Nonnull timer) {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.toValue = [NSValue valueWithCGPoint:CGPointMake(_rightGradientLayer.position.x, _rightGradientLayer.position.y - _rightGradientLayer.bounds.size.height * 0.5)];
animation.duration = circleDuarationTime;
//keep the animation last state
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
[_rightGradientLayer addAnimation:animation forKey:nil];
}];
}
對于設(shè)置mask子圖層的高度以及l(fā)ocations屬性,我沒有像這篇博客里那樣設(shè)置,我是根據(jù)自己的理解去處理的,我認為更好。首先設(shè)置layer的高度為圖片控件高度的3倍,再把漸變效果結(jié)束點設(shè)在layer高度的0.5處結(jié)束,為什么是0.5?因為圖片和layer高度比例是1:3,所以我設(shè)置開始漸變是0.4,結(jié)束在0.5,確保兩張圖片沒有重合,如果圖片上有漸變區(qū),則兩張圖片有重合,有興趣的可以改下代碼玩下。再把layer上移0.5高度確保漸變區(qū)不在圖片上。還有這些例子我也是借鑒這篇博客的,只是添加了自己的理解,以及優(yōu)化,有興趣可以下載下來對比下。
效果如下圖:

5.示例代碼:該例子是對示例4的變種
代碼如下:
- (void)configureLayerForImageview{
//1. add mask to front imageview
CAGradientLayer *tempLayer = [CAGradientLayer layer];
tempLayer.frame = CGRectMake(0, 0, _frontImageview.bounds.size.width, _frontImageview.bounds.size.height * 3);
tempLayer.colors = @[(id)[UIColor clearColor].CGColor, (id)[UIColor cyanColor].CGColor];
tempLayer.locations = @[@(0.4), @(0.5)];
_gradientLayer = tempLayer;
_frontImageview.layer.mask = _gradientLayer;
[self showBgImageview];
}
- (void)showBgImageview{
[NSTimer scheduledTimerWithTimeInterval:2 repeats:NO block:^(NSTimer * _Nonnull timer) {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.toValue = [NSValue valueWithCGPoint:CGPointMake(_gradientLayer.position.x, _gradientLayer.position.y - _gradientLayer.bounds.size.height * 0.5)];
animation.duration = 2;
//keep the animation last state
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
[_gradientLayer addAnimation:animation forKey:nil];
}];
}
效果如下圖:

當(dāng)然你改變下顏色漸變方向就可以實現(xiàn)從各個方向漸變的效果了,可以自己玩玩。
最后Demo在此,歡迎下載交流,如有寫錯的地方歡迎留言,謝謝你的垂閱!
