iOS動(dòng)畫繪制折線圖以及漸變色

有問(wèn)題聯(lián)系QQ: 652985191
有問(wèn)題聯(lián)系QQ: 652985191
有問(wèn)題聯(lián)系QQ: 652985191

說(shuō)明:

已將折線圖封裝到了一個(gè)UIView子類中,并提供了相應(yīng)的接口。若你遇到相應(yīng)的需求可以直接將文件拖到項(xiàng)目中,調(diào)用相應(yīng)的接口即可,感謝@

世俗孤島

X軸為前7天的時(shí)間,動(dòng)態(tài)獲取日期,點(diǎn)擊坐標(biāo)點(diǎn)彈出標(biāo)簽

有問(wèn)題聯(lián)系QQ: 652985191

Blog中涉及到的知識(shí)點(diǎn)

CALayer

圖層,可以簡(jiǎn)單的看做一個(gè)不接受用戶交互的UIView

每個(gè)圖層都具有一個(gè)CALayer類型mask屬性,作用與蒙版相似

Blog中主要用到的CALayer子類有

CAGradientLayer,繪制顏色漸變的背景圖層

CAShapeLayer,繪制折線圖

CAAnimation

核心動(dòng)畫的基類(不可實(shí)例化對(duì)象),實(shí)現(xiàn)動(dòng)畫操作

Quartz 2D

一個(gè)二維的繪圖引擎,用來(lái)繪制折線(Path)和坐標(biāo)軸信息(Text)

實(shí)現(xiàn)思路

折線圖視圖

整個(gè)折線圖將會(huì)被自定義到一個(gè)UIView子類中

坐標(biāo)軸繪制

坐標(biāo)軸直接繪制到折線圖視圖上,在自定義折線圖視圖的 drawRect 方法中繪制坐標(biāo)軸相關(guān)信息(線條和文字)

注意坐標(biāo)系的轉(zhuǎn)換

線條顏色漸變

失敗的方案

開(kāi)始的時(shí)候,為了實(shí)現(xiàn)線條顏色漸變,我的思考方向是,如何改變路徑(UIBezierPath)的渲染顏色(strokeColor)。但是strokeColor只可以設(shè)置一種,所以最終無(wú)法實(shí)現(xiàn)線條顏色的漸變。

成功的方案

在探索過(guò)程中找到了CALayer的CALayer類型的mask()屬性,最終找到了解決方案,即:使用UIView對(duì)象封裝漸變背景視圖(frame為折線圖視圖的減去坐標(biāo)軸后的frame),創(chuàng)建一個(gè)CAGradientLayer漸變圖層添加到背景視圖上。

創(chuàng)建一個(gè)CAShapeLayer對(duì)象,用于繪制線條,線條的渲染顏色(strokeColor)為whiteColor,填充顏色(fillColor)為clearColor,從而顯示出漸變圖層的顏色。將CAShapeLayer對(duì)象設(shè)置為背景視圖的mask屬性,即背景視圖的蒙版。

折線

使用 UIBezierPath 類來(lái)繪制折線

折線轉(zhuǎn)折處尖角的處理,使用 kCALineCapRound 與 kCALineJoinRound 設(shè)置折線轉(zhuǎn)折處為圓角

折線起點(diǎn)與終點(diǎn)的圓點(diǎn)的處理,可以直接在 UIBezierPath 對(duì)象上添加一個(gè)圓,設(shè)置遠(yuǎn)的半徑為路徑寬度的一半,從而保證是一個(gè)實(shí)心的圓而不是一個(gè)圓環(huán)

折線轉(zhuǎn)折處的點(diǎn)

折線轉(zhuǎn)折處點(diǎn)使用一個(gè)類來(lái)描述(不使用CGPoint的原因是:折線轉(zhuǎn)折處的點(diǎn)需要放到一個(gè)數(shù)組中)

坐標(biāo)軸信息

X軸、Y軸的信息分別放到一個(gè)數(shù)組中

X軸顯示的是最近七天的日期,Y軸顯示的是最近七天數(shù)據(jù)變化的幅度

動(dòng)畫

使用CABasicAnimation類來(lái)完成繪制折線圖時(shí)的動(dòng)畫

需要注意的是,折線路徑在一開(kāi)始時(shí)需要社會(huì)線寬為0,開(kāi)始繪制時(shí)才設(shè)置為適當(dāng)?shù)木€寬,保證一開(kāi)折線路徑是隱藏的

標(biāo)簽

點(diǎn)擊視圖,向折線圖視圖上添加一個(gè)標(biāo)簽(UIButton對(duì)象),顯示折線點(diǎn)的信息

具體實(shí)現(xiàn)

折線轉(zhuǎn)折處的點(diǎn)

使用一個(gè)類來(lái)描述折線轉(zhuǎn)折處的點(diǎn),代碼如下:

#import

@interfaceHLQLineChartPoint :NSObject

@property(nonatomic,assign)CGFloatx;

@property(nonatomic,assign)CGFloaty;

+ (instancetype)pointWithX:(CGFloat)X andY:(CGFloat)Y;

@end

#import"HLQLineChart.h"

@implementationHLQLineChartPoint

+ (instancetype)pointWithX:(CGFloat)X andY:(CGFloat)Y

{

HLQLineChartPoint*point = [[selfalloc]init];

point.x= X;

point.y= Y;

returnpoint;

}

@end

自定義折線圖視圖

折線圖視圖是一個(gè)自定義的UIView子類,代碼如下:

@interfaceHLQLineChart :UIView

//是否點(diǎn)擊彈出每個(gè)點(diǎn)的數(shù)值

@property(nonatomic,assign,getter=isPopupView)BOOLpopupView;

//是否倒序繪制圖形,默認(rèn)為正序

@property(nonatomic,assign,getter=isInvertedOrder)BOOLinvertedOrder;

//繪制圖形的時(shí)間,默認(rèn)是1秒

@property(nonatomic,assign)CGFloatdrawTime;

//初始化折線視圖

- (instancetype)initWithFrame:(CGRect)frame andCoordinateFigureArray:(NSMutableArray*)coordinateFigureArray

andYAxleArray:(NSMutableArray*)YAxleArray andBackgroudColor:(UIColor*)backgroudColor andFirstChangeColor:(UIColor*)FirstChangeColorandSecondChangeColor:(UIColor*)SecondChangeColor andKFontColor:(UIColor*)fontColor andcolorGradient:(BOOL)isColorGradient;

//開(kāi)始繪制折線

- (void)startDrawLineChart;

@end

//初始化

- (instancetype)initWithFrame:(CGRect)frame andCoordinateFigureArray:(NSMutableArray*)coordinateFigureArray andYAxleArray:(NSMutableArray*)YAxleArray andBackgroudColor:(UIColor*)backgroudColor andFirstChangeColor:(UIColor*)FirstChangeColorandSecondChangeColor:(UIColor*)SecondChangeColor andKFontColor:(UIColor*)fontColor andcolorGradient:(BOOL)isColorGradient

{

if(coordinateFigureArray) {

self.pointArray= coordinateFigureArray;

}

if(YAxleArray) {

self.yAxisInformationArray= YAxleArray;

}

if(self= [superinitWithFrame:frame]) {

//設(shè)置折線圖的背景顏色

if(backgroudColor) {

self.backgroundColor= backgroudColor;

}else{

self.backgroundColor=KLineBackgroudColor;

}

if(fontColor) {

self.fontColor= fontColor;

}else{

self.fontColor=KFontColor;

}

if(isColorGradient) {

self.colorGradient= isColorGradient;

}

/**設(shè)置漸變背景視圖*/

[selfdrawGradientBackgroundViewWithFirstChangeColor:FirstChangeColorandSecondChangeColor:SecondChangeColor];

/**設(shè)置折線圖層*/

[selfsetupLineChartLayerAppearance];

}

returnself;

}

繪制坐標(biāo)軸信息

- (void)drawRect:(CGRect)rect {

CGContextRefref =UIGraphicsGetCurrentContext();

//x軸信息

[self.xAxisInformationArrayenumerateObjectsUsingBlock:^(NSString*_Nonnullobj,NSUIntegeridx,BOOL*_Nonnullstop) {

NSMutableDictionary*dictM = [NSMutableDictionarydictionary];

UIFont*font = [UIFontsystemFontOfSize:8];

dictM[NSFontAttributeName] =font;

dictM[NSForegroundColorAttributeName] =self.fontColor;

CGSizeinformationSize = [objsizeWithAttributes:dictM];

//計(jì)算繪制起點(diǎn)

CGFloatdrawStartPointX =KPadding+idx*self.xAxisSpacing+(self.xAxisSpacing-informationSize.width)*0.5;

CGFloatdrawStartPointY =self.bounds.size.height-KPadding+(KPadding-informationSize.height)*0.5;

[objdrawAtPoint:CGPointMake(drawStartPointX, drawStartPointY)withAttributes:dictM];

}];

//y軸

[self.yAxisInformationArrayenumerateObjectsUsingBlock:^(NSString*_Nonnullobj,NSUIntegeridx,BOOL*_Nonnullstop) {

NSMutableDictionary*dictM = [NSMutableDictionarydictionary];

UIFont*font = [UIFontsystemFontOfSize:8];

dictM[NSFontAttributeName] =font;

dictM[NSForegroundColorAttributeName] =KFontColor;

CGSizeinformationSize = [objsizeWithAttributes:dictM];

//計(jì)算繪制起點(diǎn)

CGFloatdrawStartPointX = (KPadding-informationSize.width)*0.5;

CGFloatdrawStartPointY =self.bounds.size.height-KPadding-idx*self.yAxisSpacing-informationSize.height*0.5;

[objdrawAtPoint:CGPointMake(drawStartPointX, drawStartPointY)withAttributes:dictM];

//橫向的標(biāo)線

CGContextSetRGBStrokeColor(ref,KRedFloat,KGreenFloat,KBlueFloat,KAlaphFloat);

CGContextSetLineWidth(ref,kCoordinateLineWith);

CGContextMoveToPoint(ref,KPadding,self.bounds.size.height-KPadding-(idx*self.yAxisSpacing));

CGContextAddLineToPoint(ref,self.bounds.size.width,self.bounds.size.height-KPadding-(idx*self.yAxisSpacing));

CGContextStrokePath(ref);

}];

}

動(dòng)畫的開(kāi)始與結(jié)束

- (void)startDrawLineChart

{

//self.lineChartLayer.lineWidth = 1.0;

if(self.isDrawing) {

return;

}

if(self.tapButton) {

[self.tapButtonremoveFromSuperview];

}

self.lineChartLayer.fillColor= [UIColorclearColor].CGColor;

CABasicAnimation*pathAnimation;

//設(shè)置動(dòng)畫的相關(guān)屬性

if(self.isInvertedOrder) {

pathAnimation = [CABasicAnimationanimationWithKeyPath:@"strokeStart"];

}else{

pathAnimation = [CABasicAnimationanimationWithKeyPath:@"strokeEnd"];

}

if(self.drawTime) {

pathAnimation.duration=self.drawTime;

}else{

pathAnimation.duration=1.0;

}

pathAnimation.repeatCount=1;

pathAnimation.removedOnCompletion=NO;

pathAnimation.timingFunction= [CAMediaTimingFunctionfunctionWithName:kCAMediaTimingFunctionEaseIn];

if(self.isInvertedOrder) {

pathAnimation.fromValue= [NSNumbernumberWithInt:1.0];

pathAnimation.toValue= [NSNumbernumberWithInt:0.0];

}else{

pathAnimation.fromValue= [NSNumbernumberWithInt:0.0];

pathAnimation.toValue= [NSNumbernumberWithInt:1.0];

}

pathAnimation.delegate=self;

[self.lineChartLayeraddAnimation:pathAnimationforKey:@"strokeEnd"];

}

//動(dòng)畫開(kāi)始

- (void)animationDidStart:(CAAnimation*)anim

{

self.Drawing=YES;

}

//動(dòng)畫結(jié)束之后

- (void)animationDidStop:(CAAnimation*)anim finished:(BOOL)flag

{

if(self.iscolorGradient) {

self.lineChartLayer.fillColor= [UIColorwhiteColor].CGColor;

}

self.Drawing=NO;

}

集成折線圖視圖

//繪制漸變背景顏色

- (void)drawGradientBackgroundViewWithFirstChangeColor:(UIColor*)FirstChangeColorandSecondChangeColor:(UIColor*)SecondChangeColor

{

self.gradientBackgroundView= [[UIViewalloc]initWithFrame:CGRectMake(KPadding,0,self.bounds.size.width-KPadding,self.bounds.size.height-KPadding)];

[selfaddSubview:self.gradientBackgroundView];

self.gradientLayer= [CAGradientLayerlayer];

self.gradientLayer.frame=self.gradientBackgroundView.bounds;

self.gradientLayer.startPoint=CGPointMake(0,0.0);

self.gradientLayer.endPoint=CGPointMake(1.0,0);

//設(shè)置顏色的漸變過(guò)程

if(FirstChangeColor&&SecondChangeColor) {

self.gradientLayerColors= [NSMutableArrayarrayWithArray:@[(__bridgeid)FirstChangeColor.CGColor,

(__bridgeid)SecondChangeColor.CGColor]];

}else{

self.gradientLayerColors= [NSMutableArrayarrayWithArray:@[(__bridgeid)KFirstChangeColor.CGColor,

(__bridgeid)KSecondChangeColor.CGColor]];

}

self.gradientLayer.colors=self.gradientLayerColors;

[self.gradientBackgroundView.layeraddSublayer:self.gradientLayer];

}

- (void)setupLineChartLayerAppearance

{

UIBezierPath*path = [UIBezierPathbezierPath];

[self.pointArrayenumerateObjectsUsingBlock:^(HLQLineChartPoint*_Nonnullobj,NSUIntegeridx,BOOL*_Nonnullstop) {

//所有的點(diǎn)的位置以及圓心點(diǎn)

CGPointallPoint =CGPointMake(self.xAxisSpacing*0.5+(obj.x-1)*self.xAxisSpacing,self.bounds.size.height-KPadding-obj.y*self.yAxisSpacing);

//從x軸起點(diǎn)或終點(diǎn)的坐標(biāo)

CGPointxFromWithEndPoint =CGPointMake(self.xAxisSpacing*0.5+(obj.x-1)*self.xAxisSpacing,self.bounds.size.height-KPadding);

//折線

if(idx ==0) {

[pathmoveToPoint:xFromWithEndPoint];

[pathaddLineToPoint:allPoint];

[pathaddArcWithCenter:allPointradius:4.0startAngle:0endAngle:M_PI*2clockwise:YES];

}elseif(idx ==self.pointArray.count-1){

[pathaddArcWithCenter:allPointradius:4.0startAngle:0endAngle:M_PI*2clockwise:YES];

[pathaddLineToPoint:allPoint];

[pathaddLineToPoint:xFromWithEndPoint];

}else{

[pathaddArcWithCenter:allPointradius:4.0startAngle:0endAngle:M_PI*2clockwise:YES];

[pathaddLineToPoint:allPoint];

}

}];

self.lineChartLayer= [CAShapeLayerlayer];

self.lineChartLayer.path= path.CGPath;

self.lineChartLayer.strokeColor= [UIColorblueColor].CGColor;

if(!self.iscolorGradient) {

self.lineChartLayer.fillColor= [UIColorclearColor].CGColor;

}

self.lineChartLayer.lineWidth=2.0;

self.lineChartLayer.lineCap=kCALineCapRound;

self.lineChartLayer.lineJoin=kCALineJoinRound;

//設(shè)置折線圖層為漸變背景的mask

self.gradientBackgroundView.layer.mask=self.lineChartLayer;

}


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

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

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