使用Quartz 2D實現(xiàn)徑向漸變

由于項目原因,需要實現(xiàn)一個雷達(dá)圖,圖上的線條顏色需要從內(nèi)向外漸變,外圈上的點的顏色隨當(dāng)前部分所處區(qū)域不同而顯示不同顏色。成果圖如下,還未實現(xiàn)將折線變成曲線:

結(jié)果

線條和點都是使用UIBezierPath繪制,對于普通的繪圖UIBezierPath就足夠了,而且比較易于理解,繪圖都在layer層完成。

軸向漸變與徑向漸變

軸向漸變(Axial,也稱為線性漸變)

軸向漸變就是從一個點到另一個點的軸線漸變,所有位于垂直于軸線的某條線上的點都具有相同的值,在iOS中實現(xiàn)起來也是非常的簡單。只需要一個在CALayer上封裝好的CAGradientLayer就可以了,CAGradientLayer已經(jīng)為我們封裝好了漸變的方法,我們只需要指定需要的顏色,每種顏色的位置點,之后的漸變就交給系統(tǒng)處理。但是目前提供的字段比較少,開發(fā)者能自定義的也比較少。目前的CAGradientLayer雖然提供了type字段,但是坑爹的只支持軸向漸變,不支持徑向漸變

/* The kind of gradient that will be drawn. Currently the only allowed
 * value is `axial' (the default value). */

@property(copy) NSString *type;

CAGradientLayer實際上是給我們提供了一個漸變的背景,如果要實現(xiàn)線條等等的漸變,還需要一個CAShapeLayer。CAShapeLayer上包含路徑,將其設(shè)為mask加到CAGradientLayer,相當(dāng)于在CAGradientLayer上摳出這個路徑,大功告成。

我需要徑向漸變,怎么辦??看來只能尋求更底層的方案了,畢竟封裝的功能太少。就讓Quartz 2D來拯救世界吧。

徑向漸變(Radial)

軸向漸變是從點到點,徑向漸變就是圓到圓的輻射(圓也可以變成點)。由于系統(tǒng)分裝的兩個layer無法直接來實現(xiàn)徑向漸變,所以就只能曲線救國了。在Quartz中的為CGShadingRefCGGradientRef(其實我們只需要CGGradientRef就夠了^^)。

CGGradientRef

CGGradientRef對象是一個漸變的抽象定義,它定義了指定顏色的位置,但是未指定形狀,所以理論上我們想畫成什么樣子就可以畫成什么樣子。官方定義如下:

/* A CGGradient defines a transition between colors. The transition is
   defined over a range from 0 to 1 inclusive. A gradient specifies a color
   at location 0, one at location 1, and possibly additional colors assigned
   to locations between 0 and 1.

   A CGGradient has a color space. When a gradient is created, all colors
   specified are converted to that color space, and interpolation of colors
   occurs using the components of that color space. See the documentation of
   each creation function for more details. */

實現(xiàn)步驟也很簡單,只有幾步,代碼也不多,直接上代碼

    // 1.使用RGB模式的顏色空間
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    // 2.顏色空間,如果使用了RGB顏色空間則4個數(shù)字一組表示一個顏色,下面的數(shù)組表示4個顏色
    CGFloat colors[] = {1,0,0,1, 1,1,0,1, 0,1,0,1, 0,0,1,1};
    // 3.locations代表4個顏色的分布區(qū)域(0~1),如果需要均勻分布只需要傳入NULL
    CGFloat locations[]={0.125,0.375,0.625,0.875};
    // 4. 創(chuàng)建CGGradient對象
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, locations, 4);
    // 5. 繪制
    CGContextDrawRadialGradient (context, gradient, self.position,
                                 0, self.position, 140,
                                 kCGGradientDrawsAfterEndLocation);
    // 6. 需要釋放對象
    CGColorSpaceRelease(colorSpace);
    CGGradientRelease(gradient);

其中繪制部分參數(shù)參見官方文檔說明

/* Fill the current clipping region of `context' with a radial gradient
   between two circles defined by the center point and radius of each
   circle. The location 0 of `gradient' corresponds to a circle centered at
   `startCenter' with radius `startRadius'; the location 1 of `gradient'
   corresponds to a circle centered at `endCenter' with radius `endRadius';
   colors are linearly interpolated between these two circles based on the
   values of the gradient's locations. The option flags control whether the
   gradient is drawn before the start circle or after the end circle. */

CG_EXTERN void CGContextDrawRadialGradient(CGContextRef __nullable c,
    CGGradientRef __nullable gradient, CGPoint startCenter, CGFloat startRadius,
    CGPoint endCenter, CGFloat endRadius, CGGradientDrawingOptions options)
    CG_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

顯示效果如下圖

顯示效果

好像離成功越來越近了呢。

直接使用CAShapeLayerCAGradientLayer創(chuàng)建線條的漸變我們已經(jīng)會了,那就是同樣的道理了。既然CAGradientLayer只是提供了一個顏色背景,那我們就自定義一個自己的GradientLayer,把軸向漸變繪制到這個layer上不就行咯。

那我們就繼承一個CALayer(繼承CAGradientLayer也是一樣的,只是沒有必要),在上面繪制一下,直接上核心代碼(其實跟上面代碼一樣,在.m里重寫drawInContext方法:

- (void)drawInContext:(CGContextRef)ctx {
    UIGraphicsPushContext(ctx);
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 1.使用RGB模式的顏色空間
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    // 2.顏色空間,如果使用了RGB顏色空間則4個數(shù)字一組表示一個顏色,下面的數(shù)組表示4個顏色
    CGFloat colors[] = {1,0,0,1, 1,1,0,1, 0,1,0,1, 0,0,1,1};
    // 3.locations代表4個顏色的分布區(qū)域(0~1),如果需要均勻分布只需要傳入NULL
    CGFloat locations[]={0.125,0.375,0.625,0.875};
    // 4. 創(chuàng)建CGGradient對象
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, locations, 4);
    // 5. 繪制
    CGContextDrawRadialGradient (context, gradient, self.position,
                                 0, self.position, 140,
                                 kCGGradientDrawsAfterEndLocation);
    // 6. 需要釋放對象
    CGColorSpaceRelease(colorSpace);
    CGGradientRelease(gradient);
    
    CGContextSaveGState(context);
    CGContextRestoreGState(context);
    UIGraphicsPopContext();
}

見證奇跡的時候就要到了!哦,等等,我們還需要一個ShapeLayer。那我們就創(chuàng)建一個CAShapeLayer對象,將線條的path加到shapeLayer上。上完代碼(其實就跟使用CAShapeLayerCAGradientLayer創(chuàng)建軸向漸變一模一樣,只是將gradientLayer設(shè)置顏色和位置放到了類里面),讓我們重新來見證奇跡吧!

    self.pathLayer.frame = self.bounds;
    self.pathLayer.path = line.CGPath;
    self.pathLayer.strokeColor = [[UIColor whiteColor] CGColor];
    self.pathLayer.fillColor = nil;
    self.pathLayer.lineWidth = 2.5;
    [self.pathLayer setEdgeAntialiasingMask:kCALayerLeftEdge | kCALayerRightEdge | kCALayerBottomEdge | kCALayerTopEdge];

    [self.gradientLayer setFrame:self.bounds];
    [self.gradientLayer setMask:self.pathLayer];
    [self addSublayer:self.gradientLayer];

成功如圖:

結(jié)果

其實也挺簡單的,這樣畫一遍發(fā)現(xiàn)軸向漸變也是這樣實現(xiàn)的,而CAGradientLayer只是在CALayer上做了一次封裝,但是還未將徑向漸變封裝進(jìn)去,如果感興趣也可以仿照官方的樣子封裝一個帶有徑向漸變和軸向漸變的layer,使用type作區(qū)分。

blog更新中...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容