LMChartView-曲线,折线,柱状图

2018-02-08 10:27:45来源:https://www.jianshu.com/p/87fe45f6ed21作者:西单_夜未央人点击

分享





效果图.png

Demo源码: LMChartView



看完前任3,最大感悟: 初闻不识曲中意,再听已是曲中人



浮世三千,吾爱有三:日,月与卿。日为朝,月为暮,卿为朝朝暮暮.

大家都知道:男人越来越值钱,殊不知你爱的女人:越来越沧桑.,在这个不相信眼泪的城市,不停的打拼.你说都是为了她而奋斗,但是没有了她你就不奋斗了;你说再过几年就回去,再过几年,你难道不知道这几年是你们最为青春年少,芳华绝代的年纪,既然你做好回去的打算,而她也无心来这个钢铁混凝土的城市,何不加快你的脚步,在这美好的时间,彼此厮守.你说现在买房买车压力大,小城市没有高工资,没有工作机会.没错,但是你们的终极目标,是幸福生活.正如一个故事:http://video.tudou.com/v/XMTc4NTgyOTg0NA==.html.


悟已往之不谏,知来者之可追,实迷途其未远,觉今是而昨非

现在来说一下,这个Demo主要纪念我之前不怎么认真的地方,遇到图标都是找的第三方:
Charts
GRMustache
ZFChart
chartee
...


这个demo就根据项目需求,自定义写得,完全利用UIBezierPath画图:


pragma mark -坐标系的X,Y都是通过一条一条线画成
- (void)drawVerticalLine {
//画竖线
NSInteger spaceCount = self.shuXianAccount - 1;
CGFloat space = HorizontalLineW / spaceCount;
CGFloat startX;
CGFloat startY;
CGFloat endX;
CGFloat endY;
CGFloat lineW;
UIColor *lineColor;

for (NSInteger i = 1; i <= self.shuXianAccount; i ++) {
if (i == 1) {
lineW = Fix375(2);
lineColor = Col_0x77cecd;
startY = Fix375(7);
endY = LineStartY + VerticalLineH + lineW / 2;
} else {
lineW = Fix375(1);
lineColor = UIColorFromRGB(0xf5f5f5);
startY = LineStartY;
endY = LineStartY + VerticalLineH;
}
startX = LineStartX + (i - 1) * space - lineW / 2;
endX = LineStartX + (i - 1) * space - lineW / 2;

CGPoint startPoint = CGPointMake(startX, startY);
CGPoint endPoint = CGPointMake(endX, endY);

[self drawLineWithContext:context startPoint:startPoint endPoint:endPoint lineColor:lineColor lineWidth:lineW];

}

//X轴文字
CGFloat textX;
CGFloat textY;
CGFloat textW;
CGFloat textH;

for (NSInteger i = 1; i <= self.xTitlesArr.count; i++) {
UIColor *textColor;
if (i == self.xTitlesArr.count) {
textColor = Col_0x77cecd;
} else {
textColor = Col_0x999999;
}

NSString *xTitleStr = self.xTitlesArr[i - 1];
NSDictionary *attDic = @{NSFontAttributeName : [UIFont systemFontOfSize:Fix375(9)],
NSForegroundColorAttributeName:textColor};
CGSize xTitleStrSize = [xTitleStr sizeWithAttributes:attDic];

CGFloat textSpace = HorizontalLineW / (self.xTitlesArr.count - 1);

textX = LineStartX + (i - 1) * textSpace - xTitleStrSize.width / 2;
textY = LineStartY + VerticalLineH + Fix375(5);
textW = xTitleStrSize.width;
textH = xTitleStrSize.height;

CGRect xTitleStrRect = CGRectMake(textX ,textY , textW,textH);

[xTitleStr drawInRect:xTitleStrRect withAttributes:attDic];
}

NSString *danwei;
if (self.timeType == USkinTimeTypeDay) {
danwei = @"(h)";
} else {
danwei = @"(d)";
}

NSDictionary *danweiAttDic = @{NSFontAttributeName : [UIFont systemFontOfSize:Fix375(9)],
NSForegroundColorAttributeName:Col_0x999999};
CGSize danweiStrSize = [danwei sizeWithAttributes:danweiAttDic];
CGFloat textSpace = HorizontalLineW / (self.xTitlesArr.count - 1);
textX = LineStartX + (self.xTitlesArr.count - 1) * textSpace - danweiStrSize.width / 2 + Fix375(17);
textY = LineStartY + VerticalLineH + Fix375(5);
textW = danweiStrSize.width;
textH = danweiStrSize.height;

CGRect danweiRect = CGRectMake(textX, textY,textW, textH);
[danwei drawInRect:danweiRect withAttributes:danweiAttDic];

}

横线和竖线一样的原理


pragma mark - 画箭头
- (void)drawXArrowWithContext:(CGContextRef)context{
UIBezierPath *xArrowPath = [self xArrrowPath];
CGContextSetAllowsAntialiasing(context, YES);
CGContextSetFillColorWithColor(context, Col_0x77cecd.CGColor);
CGContextSetLineWidth(context, 1);
CGContextAddPath(context, xArrowPath.CGPath);
CGContextDrawPath(context, kCGPathFill);
}
- (void)drawYArrowWithContext:(CGContextRef)context {
UIBezierPath *xArrowPath = [self yArrrowPath];
CGContextSetAllowsAntialiasing(context, YES);
CGContextSetFillColorWithColor(context, Col_0x77cecd.CGColor);
CGContextSetLineWidth(context, 1);
CGContextAddPath(context, xArrowPath.CGPath);
CGContextDrawPath(context, kCGPathFill);
}
- (UIBezierPath *)xArrrowPath {
UIBezierPath *path = [UIBezierPath bezierPath];
CGPoint topPoint = CGPointMake(LineStartX + HorizontalLineW + Fix375(11) - Fix375(3), LineStartY + VerticalLineH - Fix375(4) + Fix375(1));
CGPoint centerPoint = CGPointMake(LineStartX + HorizontalLineW + Fix375(11), LineStartY + VerticalLineH + Fix375(1));
CGPoint bottomPoint = CGPointMake(LineStartX + HorizontalLineW + Fix375(11) - Fix375(3), LineStartY + VerticalLineH + Fix375(4) + Fix375(1));
CGPoint rightPoint = CGPointMake(LineStartX + HorizontalLineW + Fix375(11) + Fix375(10), LineStartY + VerticalLineH + Fix375(1));


[path moveToPoint:topPoint];
[path addLineToPoint:centerPoint];
[path addLineToPoint:bottomPoint];
[path addLineToPoint:rightPoint];
[path addLineToPoint:topPoint];

return path;
}
- (UIBezierPath *)yArrrowPath {
UIBezierPath *path = [UIBezierPath bezierPath];
CGPoint topPoint = CGPointMake(LineStartX - Fix375(1), 0);
CGPoint leftPoint = CGPointMake(LineStartX - Fix375(1) - Fix375(4), Fix375(10));
CGPoint centerPoint = CGPointMake(LineStartX - Fix375(1) , Fix375(10) - Fix375(3));
CGPoint rightPoint = CGPointMake(LineStartX - Fix375(1) + Fix375(4),Fix375(10));

[path moveToPoint:topPoint];
[path addLineToPoint:leftPoint];
[path addLineToPoint:centerPoint];
[path addLineToPoint:rightPoint];
[path addLineToPoint:topPoint];

return path;
}

无论画坐标还是三角形还有标度都是利用:UIBezierPath的基本用法:moveToPoint,addLineToPoint,drawInRect


接下来根据数据画图,还是基本用法
- (UIBezierPath *)linePathWihtYvalueArr:(NSArray *)yValueArr {
UIBezierPath *path = [UIBezierPath bezierPath];
for (NSInteger i = 0; i < yValueArr.count; i++) {
CGPoint onePoint = [self linePointWithYvalue:yValueArr[i] index:i];
if (i == 0) {
[path moveToPoint:onePoint];
} else {
[path addLineToPoint:onePoint];
}
}
return path;
}

下面是填充,制作渐变效果
-(void)setGradientColor {
UIBezierPath *linePath = [self fillPathWihtYvalueArr:self.yValuesArr];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.fillColor = [UIColor whiteColor].CGColor;
shapeLayer.strokeColor = Col_0x77cecd.CGColor;
shapeLayer.path = linePath.CGPath;
shapeLayer.lineWidth = 1.0f;

CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = CGRectMake(0, 0, self.width, self.height);
[gradientLayer setColors:[NSArray arrayWithObjects:(id)Col_0x77cecd.CGColor,(id)Col_0xffffff.CGColor, nil]];
gradientLayer.locations = @[@0, @1.0];
gradientLayer.borderWidth = 0.1;
[gradientLayer setMask:shapeLayer];

[self.layer addSublayer:gradientLayer];
}

中间写得还是比较仔细,尽量封装


外部使用
  NSArray *array = @[@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12",@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20",@"21",@"22",@"23"];
LMChartView1 *chart = [[LMChartView1 alloc] initWithXtitlesArr:array yTilesArr:nil timeType:USkinTimeTypeDay];
chart.backgroundColor = Col_0xffffff;
NSMutableArray *yValuesArr = @[@"0.1",@"0.11",@"0.2",@"0.23",@"0.54",@"0.5",@"0.46",@"0.37",@"0.68",@"0.9",@"0.51",@"0.11",@"0.12",@"0.13",@"0.14",@"0.15",@"0.16",@"0.17",@"0.18",@"0.19",@"0.32",@"0.21",@"0.12",@"0.02"].mutableCopy;
chart.frame = CGRectMake(0, 100, self.view.bounds.size.width, Fix375(212));
[chart fillDataWithArr:yValuesArr];
[self.view addSubview:chart];

针对曲线,我写了一个分类,因为画曲线:addCurveToPoint , addQuadCurveToPoint

贝塞尔曲线的画法是由起点、终点、控制点三个参数来画的.


我很随意的定义了三个点,为了清楚显示它们的位置,我放了三个矩形在上面以便观察,然后调用 path.moveToPoint(startPoint) 让它移动到起始点,然后调用path.addQuadCurveToPoint(endPoint, controlPoint: controlPoint) 这个方法告诉它结束点和控制点,这样它就能画出一条有弧度的线条了,如果把fillColor设置一个颜色,那么它就会变成一个很丑的形状了,示例图如下




三点画曲线.png

控制点决定了它的曲率,曲线的顶点不等于控制点的位置,具体可以看一下贝塞尔曲线的定义,你还可以使用两个控制点来画,两个控制点可以使用方法 path.addCurveToPoint(endPoint, controlPoint1: controlPoint, controlPoint2: controlPoint2)来搞定




两个控制点来画.png
这个分类添加曲度和 三点控制,四点控制
-(void)addBezierThroughPoints:(NSArray *)pointArray
{
NSAssert(pointArray.count > 0, @"没有点");

if (pointArray.count < 3) {
switch (pointArray.count) {
case 1:
{
NSValue * point0Value = pointArray[0];
CGPoint point0 = [point0Value CGPointValue];
[self addLineToPoint:point0];
}
break;
case 2:
{
NSValue * point0Value = pointArray[0];
CGPoint point0 = [point0Value CGPointValue];
NSValue * point1Value = pointArray[1];
CGPoint point1 = [point1Value CGPointValue];
[self addQuadCurveToPoint:point1 controlPoint:ControlPointForTheBezierCanThrough3Point(self.currentPoint, point0, point1)];
}
break;
default:
break;
}
}

CGPoint previousPoint = CGPointZero;

CGPoint previousCenterPoint = CGPointZero;
CGPoint centerPoint = CGPointZero;
CGFloat centerPointDistance = 0;

CGFloat obliqueRatio = 0;
CGFloat obliqueAngle = 0;

CGPoint previousControlPoint1 = CGPointZero;
CGPoint previousControlPoint2 = CGPointZero;
CGPoint controlPoint1 = CGPointZero;

previousPoint = self.currentPoint;

for (int i = 0; i < pointArray.count; i++) {

NSValue * pointIValue = pointArray[I];
CGPoint pointI = [pointIValue CGPointValue];

if (i > 0) {

previousCenterPoint = CenterPointOf(self.currentPoint, previousPoint);
centerPoint = CenterPointOf(previousPoint, pointI);

centerPointDistance = DistanceBetweenPoint(previousCenterPoint, centerPoint);

if (previousCenterPoint.x != centerPoint.x) {

obliqueRatio = (centerPoint.y - previousCenterPoint.y) / (centerPoint.x - previousCenterPoint.x);
obliqueAngle = atan(obliqueRatio);
}
else {
obliqueAngle = M_PI_2;
}

previousControlPoint2 = CGPointMake(previousPoint.x - 0.5 * self.contractionFactor * centerPointDistance * cos(obliqueAngle), previousPoint.y - 0.5 * self.contractionFactor * centerPointDistance * sin(obliqueAngle));
controlPoint1 = CGPointMake(previousPoint.x + 0.5 * self.contractionFactor * centerPointDistance * cos(obliqueAngle), previousPoint.y + 0.5 * self.contractionFactor * centerPointDistance * sin(obliqueAngle));
}

if (i == 1) {

[self addQuadCurveToPoint:previousPoint controlPoint:previousControlPoint2];
}
else if (i > 1 && i < pointArray.count - 1) {

[self addCurveToPoint:previousPoint controlPoint1:previousControlPoint1 controlPoint2:previousControlPoint2];
}
else if (i == pointArray.count - 1) {

[self addCurveToPoint:previousPoint controlPoint1:previousControlPoint1 controlPoint2:previousControlPoint2];
[self addQuadCurveToPoint:pointI controlPoint:controlPoint1];
}
else {

}

previousControlPoint1 = controlPoint1;
previousPoint = pointI;
}
}


感谢大家的支持,以后会尽量更多干货.
最后愿祝大家:有情人终成眷属,幸福永康









最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台