


說(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;
}
