在开发中我们会遇到各种统计图,或者各种绘图,本文通过对基本三大统计图:折线图、柱状图、扇形图的实现来掌握基本统计图的绘制,在下一篇文中会带来复杂一些的绘图案例分析,循序渐进达、触类旁通达到绘制各式各样图表的能力。
折线图.gif
通过自定义UIView使用自定义init方法赋值数据源,后调用 UIView的drawRect方法进行绘制。 重绘的时候 [self setNeedsDisplay]; 会自动调用 drawRect 方法。
绘制折线的时候最基本的是绘制直线、绘制圆点、绘制数据
//画线共用方法。画直线 坐标轴、横竖线、连接线
- (void)drawLine:(CGContextRef)context startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint lineColor:(UIColor *)lineColor lineWidth:(CGFloat)width {
CGContextSetShouldAntialias(context, YES ); //抗锯齿
CGColorSpaceRef Linecolorspace1 = CGColorSpaceCreateDeviceRGB();
CGContextSetStrokeColorSpace(context, Linecolorspace1);
CGContextSetLineWidth(context, width);
CGContextSetStrokeColorWithColor(context, lineColor.CGColor);
CGContextMoveToPoint(context, startPoint.x, startPoint.y);
CGContextAddLineToPoint(context, endPoint.x, endPoint.y);
CGContextStrokePath(context);
CGColorSpaceRelease(Linecolorspace1);
}
作者:進无尽
链接:https://www.jianshu.com/p/a3b8fbb00170
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
使用CAShapeLayer 和 UIBezierPath,可以实现动态绘制的动画效果。
使用for循环绘制多条折线的步骤(for 循环一次的情况下):
初始化一个 CAShapeLayer ,加载在 当前的layer上。
初始化 UIBezierPath 供CAShapeLayer 使用;
使用 for循环再绘制余下的每一个圆点,确保每一个圆点都在 CAShapeLayer 的上层,
同时对UIBezierPath添加每一个余下的点路径,这样就不会在绘制折线的时候,影响到圆点的展示。
使用CABasicAnimation 利用layer 的strokeEnd属性动态绘制,不使用动画时,会直接一下绘制完成。
for (int i=0; i<_yValues.count; i++) {
//划线
CAShapeLayer *_chartLine = [CAShapeLayer layer];
_chartLine.lineCap = kCALineCapRound;
_chartLine.lineJoin = kCALineJoinBevel;
_chartLine.fillColor = [[UIColor whiteColor] CGColor];
_chartLine.lineWidth = 2.0;
[self.layer addSublayer:_chartLine];
UIBezierPath *progressline = [UIBezierPath bezierPath];
CGFloat firstValue = [[childAry objectAtIndex:0] floatValue];
CGFloat xPosition = (UUYLabelwidth + _xLabelWidth/2.0);
CGFloat chartCavanHeight = self.frame.size.height - UULabelHeight*3
绘制圆点
拼接路径
moveToPoint 设置起点
[progressline moveToPoint:CGPointMake(xPosition, chartCavanHeight - grade * chartCavanHeight+UULabelHeight)];
[progressline setLineWidth:2.0];
[progressline setLineCapStyle:kCGLineCapRound];
[progressline setLineJoinStyle:kCGLineJoinRound];
NSInteger index = 0;
for (NSString * valueString in childAry) {
float grade =([valueString floatValue]-_yValueMin) / ((float)_yValueMax-_yValueMin);
CGPoint point = CGPointMake(xPosition+index*_xLabelWidth, chartCavanHeight - grade * chartCavanHeight+UULabelHeight);
拼接路径
[progressline addLineToPoint:point];
绘制圆点
index += 1;
}
_chartLine.path = progressline.CGPath;
_chartLine.strokeColor = [UUGreen CGColor];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnimation.duration = childAry.count*0.4;
pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[_chartLine addAnimation:pathAnimation forKey:@""];
}
作者:進无尽
链接:https://www.jianshu.com/p/a3b8fbb00170
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
** 绘制虚线**
绘制虚线
CAShapeLayer设置 虚线宽,线间距 数组第一个是虚线中实现的长度,第二个是虚线中空白的宽度。
设置一个 UIBezierPath 绘制好路径赋值给 CAShapeLayer即可。
[shapeLayer setLineDashPattern:[NSArray arrayWithObjects:[NSNumber numberWithInt:5], [NSNumber numberWithInt:5], nil]];
避免出现上图上的效果图,两种方法:
柱状图.gif
自定义 UUBar类,展示的是单个柱状的效果,在 UUBarChart类中调用生成多个柱状的效果。
UUBar中 使用CAShapeLayer 、UIBezierPath、CABasicAnimation可实现动态柱状图
CAShapeLayer设置
_chartLine.fillColor = [[UIColor whiteColor] CGColor];
_chartLine.lineWidth = self.frame.size.width;
路线设置
UIBezierPath *progressline = [UIBezierPath bezierPath];
起点
[progressline moveToPoint:CGPointMake(self.frame.size.width/2.0, self.frame.size.height+30)];
终点
[progressline addLineToPoint:CGPointMake(self.frame.size.width/2.0, (1 - grade) * self.frame.size.height+15)];
[progressline setLineWidth:1.0];
[progressline setLineCapStyle:kCGLineCapSquare];
_chartLine.path = progressline.CGPath;
if (_barColor) {
_chartLine.strokeColor = [_barColor CGColor];
}else{
_chartLine.strokeColor = [UUGreen CGColor];
}
动画设置
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnimation.duration = 1.5;
pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
pathAnimation.autoreverses = NO;
[_chartLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
使用Core Graphics绘制扇形
- (void)drawRect:(CGRect)rect
{
一圆周的弧度(360度)
CGFloat allAngle = M_PI*2;
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(ctx, 160, 300);
CGContextSetFillColor(ctx, CGColorGetComponents( [UIColor redColor].CGColor));
参数:画布,x,y为圆点坐标,radius半径,startAngle为开始的弧度,endAngle为 结束的弧度,clockwise 0为顺时针,1为逆时针。
CGContextAddArc(ctx, 160, 300, 100, 0, allAngle*0.3, 0);
CGContextFillPath(ctx);
}
使用UIBezierPath绘制扇形 在我这篇文章中我说过:UIBezierPath是在 UIKit 中的一个类,继承于NSObject,可以创建基于矢量的路径.此类是Core Graphics框架关于path的一个OC封装。所以 UIBezierPath 是基于 Core Graphics 实现的一项绘图技术。所以使用UIBezierPath当然也是可以绘制图形的,只是必须在 drawRect 方法中,不可在其他位置。
- (void)drawRect:(CGRect)rect
{
UIBezierPath *arcPath = [UIBezierPath bezierPath];
[arcPath moveToPoint:CGPointMake(160, 200)];
[arcPath addArcWithCenter:CGPointMake(160, 200) radius:50 startAngle:0 endAngle:M_PI * 0.3 clockwise:YES];
[[UIColor brownColor] set];
[arcPath fill];
[arcPath stroke];
}
动态扇形图.gif
CGPoint center = CGPointMake(160, 300);
CGFloat radius = 50;
//这里的思路是只设置一条路径供所有的CAShapeLayer使用,实际上 当前这条
//UIBezierPath 画的是一个圆,控制每个CAShapeLayer 的strokeStart和strokeEnd 即可定区域绘制了
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center
radius:radius
startAngle:-M_PI_2
endAngle:M_PI_2*3
clockwise:YES];
//fillColor必须设置为clearColor
circle.fillColor = [UIColor clearColor].CGColor;
circle.strokeColor = [UIColor magentaColor].CGColor;
//lineWidth必须设置为radius的2倍
circle.lineWidth = radius*2;
circle.strokeStart = 0;
circle.strokeEnd = 0.6;
circle.path = path.CGPath;
[self.view.layer addSublayer:circle];
CAShapeLayer *circle1 = [CAShapeLayer layer];
circle1.fillColor = [UIColor clearColor].CGColor;
circle1.strokeColor = [UIColor purpleColor].CGColor;
circle1.lineWidth = radius*2;
circle1.strokeStart = 0.6;
circle1.strokeEnd = 0.8;
circle1.path = path.CGPath;
[self autoDraw:circle :0 :0.6 :1];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.view.layer addSublayer:circle1];
[self autoDraw:circle1 :0.6 :0.8 :0.5];
});
- (void)autoDraw :(CALayer *)layer :(CGFloat)x :(CGFloat)y :(CGFloat)duration
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.duration = duration;
animation.fromValue = [NSNumber numberWithFloat:x];
animation.toValue = [NSNumber numberWithFloat:y];;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[layer addAnimation:animation forKey:@"circleAnimation"];
}
使用 fillColor
UIBezierPath *path = [UIBezierPath bezierPath];
设定一个起点
[path moveToPoint:center];
画圆弧 M_PI_2 90度,从水平右边开始
[path addArcWithCenter:center radius:radius startAngle:-M_PI_2
endAngle:M_PI_2*0.3
clockwise:YES];
circle.fillColor = [UIColor magentaColor].CGColor;
circle.strokeColor = [UIColor clearColor].CGColor;
circle.strokeStart = 0;
circle.strokeEnd = 0.6;
circle.path = path.CGPath;
[self.view.layer addSublayer:circle];
考虑到篇幅,这篇文就只介绍折线、柱状、扇形这三大基本统计图的绘制,原理都是一样的,只是需要一些思路和技巧,下篇会带来一些复杂些的绘图案例分析。