再简单不过的折线图实现方法

涉及知识点:

CGContextRef    //  联系图形上下文,可以说是画布的存在吧

UIBezierPath     //  贝塞尔曲线,画啥都可以,没有画不了的,只有想不到的

CAShapeLayer    //  配合贝塞尔曲线使用的shape图层,贝塞尔的“最佳搭档”

CAGradientLayer    //  渐变图层

CABasicAnimation    // (基础)Core Animation 显示动画

最近想弄一下比较实在的东西,然后有个朋友跟我说:“要不你就弄弄贝塞尔吧”。搞就搞吧,只有贝塞尔的话就有点单调,干脆就加点其他东西实现个简单的折线图吧,所以这个demo就出来了,不过这仅仅是小demo,完整的版本还没写好封装。话不多说,整个demo分四步:第一步,实现横、轴坐标轴;第二步,虚线与渐变层的实现;第三步,描点连线。直接上图贴代码,简单快捷。



第一步  X 、Y轴的实现

直接上第一张图


X 、Y轴的实现

创建一个类,继承于UIView,在.m文件中的- (void)drawRect:(CGRect)rect 方法中画出我们所需要的X轴Y轴坐标线。在这个方法中,我们所实现的视图能够在view上重新描画展示。

- (void)drawRect:(CGRect)rect {

// Drawing code

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGContextSetRGBStrokeColor(context, 97/255.0, 150/255.0, 198/255.0, 1);
CGContextMoveToPoint(context, boundX, boundY);
// Y 轴
CGContextAddLineToPoint(context, boundX, rect.size.height - boundY);
// X 轴
CGContextAddLineToPoint(context, rect.size.width -  boundX, rect.size.height - boundY);
// X轴 箭头
CGContextMoveToPoint(context, rect.size.width -  boundX - 5, rect.size.height - boundY -5);
CGContextAddLineToPoint(context, rect.size.width -  boundX, rect.size.height - boundY);
CGContextAddLineToPoint(context, rect.size.width -  boundX - 5, rect.size.height - boundY + 5);
// Y轴 箭头
CGContextMoveToPoint(context, boundX - 5, boundY + 5);
CGContextAddLineToPoint(context, boundX, boundY);
CGContextAddLineToPoint(context, boundX + 5, boundY + 5);
// 结束绘制
CGContextStrokePath(context);
}

X轴与Y轴的数据实现,在这里分别写了两个实例方法,在initWithFram:初始化方法中调用

#pragma mark 创建x轴的数据

- (void)createXLine{

CGFloat  month = 12;

for (NSInteger i = 0; i < month; i++) {

UILabel * x_label = [[UILabel alloc]initWithFrame:CGRectMake((self.frame.size.width - 2 * boundX)/month * i + boundX, self.frame.size.height - boundY + boundY*0.2, (self.frame.size.width - 2 * boundX)/month- 5, boundY/2)];

//      LabelMonth.backgroundColor = [UIColor greenColor];

x_label.tag = 1000 + i;

x_label.text = [NSString stringWithFormat:@"%ld月",i+1];

x_label.font = [UIFont systemFontOfSize:8];

x_label.transform = CGAffineTransformMakeRotation(M_PI * 0.3);

x_label.textColor = [UIColor whiteColor];

[self addSubview:x_label];

}

}

#pragma mark 创建y轴数据

- (void)createYLine{

CGFloat Y_Value = 6;

for (NSInteger i = 0; i < Y_Value; i++) {

UILabel * y_label = [[UILabel alloc]initWithFrame:CGRectMake(0, (self.frame.size.height - 2 * boundY)/Y_Value * i + boundX, boundY, boundY/2.0)];

//  labelYdivision.backgroundColor = [UIColor greenColor];

y_label.tag = 2000 + i;

y_label.text = [NSString stringWithFormat:@"%.0f",(Y_Value - i)*100];

y_label.font = [UIFont systemFontOfSize:10];

y_label.textColor = [UIColor whiteColor];

y_label.textAlignment = NSTextAlignmentCenter;

[self addSubview:y_label];

}

}

第二步  虚线与渐变图层背景的设置

在这里我们需要设置三个属性,分别是:1.渐变的背景视图(添加到当前视图上), 2.渐变图层(作用于背景视图的layer层),3.颜色数组(添加渐变图层的颜色属性数值,也就是渐变哪几种颜色,这里我用了两种比较低调灰沉一点的色调)

有一点要注意的是,我这里画的虚线是通过CAShapeLayer的 lineDashPattern属性来设置的


属性

同样编写两个方法:- (void)drawGradientBackgroundView 设置渐变层背景  和 - (void)setDashLine 设置虚线,并在初始化方法中调用

- (void)drawGradientBackgroundView {

// 渐变背景视图(不包含坐标轴)

self.gradient_backgroundView = [[UIView alloc] initWithFrame:CGRectMake(boundX, boundY, self.bounds.size.width - boundX*2, self.bounds.size.height - 2*boundY)];

[self addSubview:self.gradient_backgroundView];

/** 创建并设置渐变背景图层 */

//初始化CAGradientlayer对象,使它的大小为渐变背景视图的大小

self.gradient_layer = [CAGradientLayer layer];

self.gradient_layer.frame = self.gradient_backgroundView.bounds;

//设置渐变区域的起始和终止位置(范围为0-1),即渐变路径

self.gradient_layer.startPoint = CGPointMake(0, 0.0);

self.gradient_layer.endPoint = CGPointMake(1.0, 0.0);

//设置颜色的渐变过程

// [UIColor colorWithRed:67 / 255.0 green:106 / 255.0 blue:140 / 255.0 alpha:1.0]

// [UIColor colorWithRed:59 / 255.0 green:92 / 255.0 blue:120 / 255.0 alpha:1.0]

self.colors_arr = [NSMutableArray arrayWithArray:@[(__bridge id)[UIColor colorWithRed:95 / 255.0 green:148 / 255.0 blue:195 / 255.0 alpha:0.4].CGColor, (__bridge id)[UIColor colorWithRed:59 / 255.0 green:92 / 255.0 blue:120 / 255.0 alpha:0.4].CGColor]];

self.gradient_layer.colors = self.colors_arr;

//将CAGradientlayer对象添加在我们要设置背景色的视图的layer层

[self.gradient_backgroundView.layer addSublayer:self.gradient_layer];

}

- (void)setDashLine{

for (NSInteger i = 1;i < 6; i++ ) {

UILabel * label1 = (UILabel*)[self viewWithTag:2000 + i];//获取Y轴数据label的位置根据其位置画横虚线

UIBezierPath * path1 = [UIBezierPath bezierPath];

[path1 moveToPoint:CGPointMake( 0, label1.frame.origin.y - boundY)];

[path1 addLineToPoint:CGPointMake(self.frame.size.width - 2 * boundX,label1.frame.origin.y - boundY)];

CAShapeLayer *dashLayer = [CAShapeLayer layer];

dashLayer.strokeColor = [UIColor whiteColor].CGColor;

dashLayer.fillColor = [UIColor clearColor].CGColor;

// 设置线条宽度

dashLayer.lineWidth = 1.0;

//  设置虚线  每间隔十个画一条线,总共十条

dashLayer.lineDashPattern = @[@10, @10];

dashLayer.path = path1.CGPath;

[self.gradient_backgroundView.layer addSublayer:dashLayer];

}

}

渐变背景与虚线

第三步  描点连线

由于是懒得在电脑上安装GIF的制作应用,所以就上不了动态图了,简单贴一张完整图片就好


完整视图

- (void)drawLine{

UILabel * label = (UILabel*)[self viewWithTag:1000];//根据横坐标上面的label 获取直线关键点的x 值

UIBezierPath * path = [[UIBezierPath alloc]init];

self.path1 = path;

[path moveToPoint:CGPointMake( label.frame.origin.x - boundX + 7, (600 -arc4random()%600) /600.0 * (self.frame.size.height - boundY*2 )  )];

//创建折现点标记

for (NSInteger i = 1; i< 12; i++) {

UILabel * label1 = (UILabel*)[self viewWithTag:1000 + i];

CGFloat  arc = arc4random()%600;  //折线点目前给的是随机数

[path addLineToPoint:CGPointMake(label1.frame.origin.x - boundX,  (600 -arc) /600.0 * (self.frame.size.height - boundY*2 ) )];

UILabel * falglabel = [[UILabel alloc]initWithFrame:CGRectMake(label1.frame.origin.x , (600 -arc) /600.0 * (self.frame.size.height - boundY*2 )+ boundY  , 30, 15)];

//  falglabel.backgroundColor = [UIColor blueColor];

falglabel.tag = 3000+ i;

falglabel.text = [NSString stringWithFormat:@"%.1f",arc];

falglabel.font = [UIFont systemFontOfSize:8.0];

[self addSubview:falglabel];

}

[path stroke];

self.lineChartLayer = [CAShapeLayer layer];

self.lineChartLayer.path = path.CGPath;

self.lineChartLayer.strokeColor = [UIColor whiteColor].CGColor;

self.lineChartLayer.fillColor = [[UIColor clearColor] CGColor];

self.lineChartLayer.lineWidth = 2;

self.lineChartLayer.lineCap = kCALineCapRound;

self.lineChartLayer.lineJoin = kCALineJoinRound;

[self.gradient_backgroundView.layer addSublayer:self.lineChartLayer];//直接添加导视图上

CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];

pathAnimation.duration = 3;

pathAnimation.repeatCount = 1;

pathAnimation.removedOnCompletion = YES;

pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];

pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];

// 设置动画代理,动画结束时添加一个标签,显示折线终点的信息

pathAnimation.delegate = self;

[self.lineChartLayer addAnimation:pathAnimation forKey:@"strokeEnd"];

}


虽然这个demo做的是不太美观,不过整体都拆分开了几个方法调用,方便了扩展,后续会为大家继续补充其他功能,还有完整封装好的代码。

附上Github的链接:

https://github.com/zengxcgg/LineChartDemo.git 

推荐阅读更多精彩内容

  • 1、禁止手机睡眠[UIApplication sharedApplication].idleTimerDisabl...
    DingGa阅读 592评论 1 6
  • 故曳黄絁学道妆, 数茎风外婉清扬。 非关水土天然活, 略润烟霞别自芳。 高髻步摇金雀艳, 深宫龋齿玉鱼凉。 何人为...
    影曳香弄阅读 372评论 1 0
  • 我庆幸曾经一起走过的路 我也不后悔那时的争吵 那时常去的街道 如今变了模样 那日阳光正好 你笑,我念 你说蓝色忠贞...
    西行看海阅读 56评论 0 0
  • 不再敢喜欢谁了,因为喜欢便迎来失望,因为喜欢便患得患失,因为喜欢便开始漫长的自虐。我就是这样的,笨蛋。人家对我好一...
    c621a388db07阅读 73评论 0 0
  • 中午从桌前起身,伸了伸懒腰望向窗外,金黄色的油菜花在我眼前展开,给大地铺上了一层金黄的地毯。这地毯清新、明艳、动人...
    千寻瀑日记阅读 149评论 7 4