由于在工作中需要绘制统计的折线图数据,在网上搜了很多第三方,都写的很厉害,但是作为实际需求,这些强大的第三方有两个弊端,一是体积太大,占用内存都不算小,二是,看到的大部分都是需要各种编译封装,再学习他们的api,很麻烦(因为有之前使用linPhone的惨痛经历)所以想到了为啥不自己写一个,于是就有了现在这个代码。
废话不多说先上代码核心代码:
/** 根据点的数组去设置一个折线统计图 @param LCVFrame 折线图的位置信息 @param MLColor 折线图的xy轴的颜色 @param LCColor 折线图的折线的颜色 @param pointArr 点的数组 @return 返回一个折线图 */
- (CYKlineChartView *)_creatLineChartViewWithFrame:(CGRect)LCVFrame andMainLineColor:(UIColor *)MLColor andLineChartColor:(UIColor *)LCColor andFillColor:(UIColor *)fillColor forPointArray:(NSArray *)pointArr;
- (CYKlineChartView*)_creatLineChartViewWithFrame:(CGRect)LCVFrame andMainLineColor:(UIColor*)MLColor andLineChartColor:(UIColor*)LCColor andFillColor:(nonnullUIColor*)fillColor forPointArray:(nonnullNSArray*)pointArr{
self.frame= LCVFrame;
//先画一个框
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
CGPointlastPoint = [pointArr.lastObjectCGPointValue];
//然后根据最后一个点 我们可以确定我们x轴点起始点的值
CGPointxStarPoint =CGPointMake(lastPoint.x, LCVFrame.size.height);
//然后根据数组中的第一个点 我们可以确定y轴的起始点的值
CGPointstarPoint = [pointArr.firstObjectCGPointValue];
CGPointyStarPoint =CGPointMake(0, starPoint.y);
//接下来先画x,y轴的两条线
[bezierPathmoveToPoint:xStarPoint];
[bezierPathaddLineToPoint:CGPointMake(0, LCVFrame.size.height)];
[bezierPathaddLineToPoint:yStarPoint];
for(NSValue*pointValueinpointArr) {
CGPointchartPoint = [pointValueCGPointValue];
[bezierPathaddLineToPoint:chartPoint];
}
//线条填色
CAShapeLayer *layer1 = [CAShapeLayer new];
layer1.strokeColor= LCColor.CGColor;
layer1.fillColor= fillColor.CGColor;
layer1.lineWidth=1;
layer1.path= [bezierPathCGPath];
[self.layeraddSublayer:layer1];
//在画xy轴将其覆盖掉
UIBezierPath *bezierPath2 = [UIBezierPath bezierPath];
[bezierPath2moveToPoint:CGPointMake(LCVFrame.size.width,0)];
[bezierPath2addLineToPoint:CGPointMake(0, 0)];
[bezierPath2addLineToPoint:CGPointMake(0, LCVFrame.size.height)];
CGFloaty = LCVFrame.size.height/5;
for(inti =0; i<6; i++) {
UIBezierPath *bezierPathH = [UIBezierPath bezierPath]; //横向的网格线
[bezierPathHmoveToPoint:CGPointMake(0, y*i)];
[bezierPathHaddLineToPoint:CGPointMake(LCVFrame.size.width, y*i)];
CAShapeLayer *layerH = [CAShapeLayer new];
layerH.strokeColor = [UIColor grayColor].CGColor;
layerH.lineWidth=1;
layerH.path= [bezierPathHCGPath];
[self.layeraddSublayer:layerH];
}
//覆盖y轴
UIBezierPath *bezierPathV = [UIBezierPath bezierPath]; //横向的网格线
[bezierPathVmoveToPoint:CGPointMake(0,0)];
[bezierPathVaddLineToPoint:CGPointMake(0, LCVFrame.size.height)];
CAShapeLayer *layerV = [CAShapeLayer new];
layerV.strokeColor = [UIColor whiteColor].CGColor;
layerV.lineWidth=1;
layerV.path= [bezierPathVCGPath];
[self.layeraddSublayer:layerV];
//最后再次画点覆盖所有的涂层
for(NSValue*pointValueinpointArr) {
CGPointchartPoint = [pointValueCGPointValue];
UIBezierPath * bezierPathP = [UIBezierPath bezierPathWithArcCenter:chartPoint radius:2 startAngle:0.f endAngle:M_PI * 2 clockwise:YES];
CAShapeLayer *layerP = [CAShapeLayer new];
layerP.strokeColor = [UIColor redColor].CGColor;
layerP.lineWidth=2;
layerP.path= [bezierPathPCGPath];
[self.layeraddSublayer:layerP];
}
return self;
}
解释一下,这里的侧重点有几个,首先我们会传入一个有关于我们要显示的结果值的数组,这个数组我们经过占比计算之后将数值直接转化为在这个视图上我们要显示的点的位置。
其次我们的思路就是一个图层覆盖一个图层的去绘制出最终我们需要的折线图,这就需要我们清楚的了解我们绘制图层的顺序是什么,这个很重要。
以此为例,我需要去绘制一个带有填充区域的折线图,所以首先我们要画的就是这个区域,一个由x轴->y轴->数组中的每个点的一个非闭合区域。
那么它就会出现如图中所示的x轴,y轴,折线,填充区域的第一个图层
接着我们需要进行第二部分的图层覆盖,绘制我们自己所需要的x轴、y轴的图层,颜色样式可以自定义,怎么需要怎么画,那么就会出现上图中x轴y轴和剩下的辅助线(这里因为项目需要 我直接把y轴做成与底色一致的)。
最后我们要进行第三部分的图层覆盖,绘制我们需要的点的样式,这里我用的是圆点,你可以自定义方形也好,五角星也好,甚至直接插入图片也可以,这一步一定呀最后做,这样能够保证显示的全面性。
后续如果还需要添加其他的数值或者是y轴的坐标,都可以再在此基础上进行添加。
这里要注意一个问题,就是在数组存储点的位置的时候我们需要先转化为nsvalue类型的进行操作,结构体是不能直接写入数组的。
此文到此就告一段落的,后续会有其他的贝塞尔曲线绘制控件图层的用法,欢迎大家继续探讨。