iOS動畫繪制折線圖以及漸變色

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

說明:

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

世俗孤島

X軸為前7天的時間,動態(tài)獲取日期,點擊坐標點彈出標簽

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

Blog中涉及到的知識點

CALayer

圖層别凹,可以簡單的看做一個不接受用戶交互的UIView

每個圖層都具有一個CALayer類型mask屬性邓厕,作用與蒙版相似

Blog中主要用到的CALayer子類有

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

CAShapeLayer,繪制折線圖

CAAnimation

核心動畫的基類(不可實例化對象)详恼,實現(xiàn)動畫操作

Quartz 2D

一個二維的繪圖引擎补君,用來繪制折線(Path)和坐標軸信息(Text)

實現(xiàn)思路

折線圖視圖

整個折線圖將會被自定義到一個UIView子類中

坐標軸繪制

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

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

線條顏色漸變

失敗的方案

開始的時候昧互,為了實現(xiàn)線條顏色漸變挽铁,我的思考方向是,如何改變路徑(UIBezierPath)的渲染顏色(strokeColor)敞掘。但是strokeColor只可以設置一種叽掘,所以最終無法實現(xiàn)線條顏色的漸變。

成功的方案

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

創(chuàng)建一個CAShapeLayer對象疯潭,用于繪制線條赊堪,線條的渲染顏色(strokeColor)為whiteColor,填充顏色(fillColor)為clearColor竖哩,從而顯示出漸變圖層的顏色哭廉。將CAShapeLayer對象設置為背景視圖的mask屬性,即背景視圖的蒙版相叁。

折線

使用 UIBezierPath 類來繪制折線

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

折線起點與終點的圓點的處理,可以直接在 UIBezierPath 對象上添加一個圓增淹,設置遠的半徑為路徑寬度的一半椿访,從而保證是一個實心的圓而不是一個圓環(huán)

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

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

坐標軸信息

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

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

動畫

使用CABasicAnimation類來完成繪制折線圖時的動畫

需要注意的是成玫,折線路徑在一開始時需要社會線寬為0,開始繪制時才設置為適當?shù)木€寬拳喻,保證一開折線路徑是隱藏的

標簽

點擊視圖哭当,向折線圖視圖上添加一個標簽(UIButton對象),顯示折線點的信息

具體實現(xiàn)

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

使用一個類來描述折線轉(zhuǎ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

自定義折線圖視圖

折線圖視圖是一個自定義的UIView子類钦勘,代碼如下:

@interfaceHLQLineChart :UIView

//是否點擊彈出每個點的數(shù)值

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

//是否倒序繪制圖形,默認為正序

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

//繪制圖形的時間,默認是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;

//開始繪制折線

- (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]) {

//設置折線圖的背景顏色

if(backgroudColor) {

self.backgroundColor= backgroudColor;

}else{

self.backgroundColor=KLineBackgroudColor;

}

if(fontColor) {

self.fontColor= fontColor;

}else{

self.fontColor=KFontColor;

}

if(isColorGradient) {

self.colorGradient= isColorGradient;

}

/**設置漸變背景視圖*/

[selfdrawGradientBackgroundViewWithFirstChangeColor:FirstChangeColorandSecondChangeColor:SecondChangeColor];

/**設置折線圖層*/

[selfsetupLineChartLayerAppearance];

}

returnself;

}

繪制坐標軸信息

- (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];

//計算繪制起點

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];

//計算繪制起點

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];

//橫向的標線

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);

}];

}

動畫的開始與結(jié)束

- (void)startDrawLineChart

{

//self.lineChartLayer.lineWidth = 1.0;

if(self.isDrawing) {

return;

}

if(self.tapButton) {

[self.tapButtonremoveFromSuperview];

}

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

CABasicAnimation*pathAnimation;

//設置動畫的相關屬性

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"];

}

//動畫開始

- (void)animationDidStart:(CAAnimation*)anim

{

self.Drawing=YES;

}

//動畫結(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);

//設置顏色的漸變過程

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) {

//所有的點的位置以及圓心點

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

//從x軸起點或終點的坐標

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;

//設置折線圖層為漸變背景的mask

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

}


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市亚亲,隨后出現(xiàn)的幾起案子彻采,更是在濱河造成了極大的恐慌,老刑警劉巖朵栖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件颊亮,死亡現(xiàn)場離奇詭異柴梆,居然都是意外死亡陨溅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門绍在,熙熙樓的掌柜王于貴愁眉苦臉地迎上來门扇,“玉大人,你說我怎么就攤上這事偿渡【始模” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵溜宽,是天一觀的道長吉拳。 經(jīng)常有香客問我,道長适揉,這世上最難降的妖魔是什么留攒? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任煤惩,我火速辦了婚禮,結(jié)果婚禮上炼邀,老公的妹妹穿的比我還像新娘魄揉。我一直安慰自己,他們只是感情好拭宁,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布洛退。 她就那樣靜靜地躺著,像睡著了一般杰标。 火紅的嫁衣襯著肌膚如雪兵怯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天腔剂,我揣著相機與錄音摇零,去河邊找鬼。 笑死桶蝎,一個胖子當著我的面吹牛驻仅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播登渣,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼噪服,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了胜茧?” 一聲冷哼從身側(cè)響起粘优,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎呻顽,沒想到半個月后雹顺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡廊遍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年嬉愧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喉前。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡没酣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出卵迂,到底是詐尸還是另有隱情裕便,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布见咒,位于F島的核電站偿衰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜下翎,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一囱嫩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧漏设,春花似錦墨闲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至犬性,卻和暖如春瞻离,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背乒裆。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工套利, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鹤耍。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓肉迫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親稿黄。 傳聞我的和親對象是個殘疾皇子喊衫,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

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