iOS CAGradientLayer漸變效果使用及個人理解

文章結(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是最好理解的,如下。
  1. 示例代碼:設(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é)束漸變。

  1. 示例代碼:設(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í)慣,除非要改變顏色漸變的方向,否則這兩個屬性我一般不會去改變。這是個人見解,如有高見或者有誤的地方歡迎交流

使用范例及代碼講解

  1. 示例代碼:給圖片添加漸變顏色
- (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下載地址。

  1. 示例代碼:進度條
    通過設(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];
}

效果如下圖:


  1. 示例代碼:在圓形軌跡上動態(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];
    }];
}

最終效果如下圖


  1. 示例代碼:兩張圖片大小一樣且在同一位置,先左后右動態(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在此,歡迎下載交流,如有寫錯的地方歡迎留言,謝謝你的垂閱!

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

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

  • 在iOS中隨處都可以看到絢麗的動畫效果,實現(xiàn)這些動畫的過程并不復(fù)雜,今天將帶大家一窺ios動畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,690評論 6 30
  • 轉(zhuǎn)載:http://www.itdecent.cn/p/32fcadd12108 每個UIView有一個伙伴稱為l...
    F麥子閱讀 6,583評論 0 13
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現(xiàn)這些動畫的過程并不復(fù)雜,今天將帶大家一窺iOS動畫全貌。在這里你可以看...
    F麥子閱讀 5,270評論 5 13
  • 每個UIView有一個伙伴稱為layer,一個CALayer。UIView實際上并沒有把自己畫到屏幕上;它繪制本身...
    shenzhenboy閱讀 3,255評論 0 17
  • Core Animation其實是一個令人誤解的命名。你可能認為它只是用來做動畫的,但實際上它是從一個叫做Laye...
    小貓仔閱讀 3,964評論 1 4

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