由于項目原因,需要實現(xiàn)一個雷達(dá)圖,圖上的線條顏色需要從內(nèi)向外漸變,外圈上的點的顏色隨當(dāng)前部分所處區(qū)域不同而顯示不同顏色。成果圖如下,還未實現(xiàn)將折線變成曲線:
線條和點都是使用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中的為CGShadingRef
和CGGradientRef
(其實我們只需要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);
顯示效果如下圖
好像離成功越來越近了呢。
直接使用CAShapeLayer
和CAGradientLayer
創(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上。上完代碼(其實就跟使用CAShapeLayer
和CAGradientLayer
創(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];
成功如圖:
其實也挺簡單的,這樣畫一遍發(fā)現(xiàn)軸向漸變也是這樣實現(xiàn)的,而CAGradientLayer
只是在CALayer
上做了一次封裝,但是還未將徑向漸變封裝進(jìn)去,如果感興趣也可以仿照官方的樣子封裝一個帶有徑向漸變和軸向漸變的layer,使用type作區(qū)分。
blog更新中...