适用人群:iOS开发人员。
本文内容:使用UIBezierPath和CAShareLayer绘制简单的折线图。
平常,找个第三方的折线图,东改西改,很难做到设计图上的效果。与其折腾,还不如自己画个折线图好了,又省事又美观。其实画图也很简单,无非画路径、上颜色而已。
本文基于一个小例子讲解如何画简单的折线图,目录如下:
1.效果图展示
2.绘图思路入门
3.代码粘贴
1.1.效果图展示
先来展示下效果图:
2.绘图思路入门
就像画画一样,一步一步来。
(1)首先,给折线图区块定一个位置大小,这样方便绘制一些。这个区块的下边界即为x轴,左边界即为y轴,如下图所示:
chartX = LCSCREEN_WIDTH/375*60;
chartY = LCSCREEN_WIDTH/375*108;
chartWidth = LCSCREEN_WIDTH-chartX-15.0;
chartHeight = LineChartViewHeight - LCSCREEN_WIDTH/375*152;
(2)折线图区域定好了,就可以从绘制坐标轴开始了。坐标轴很简单,一条线搞定,三点一折线,代码如下:
// 绘制坐标轴
UIBezierPath *axis = [[UIBezierPath alloc] init];
[axis moveToPoint:CGPointMake(chartX, chartY)];//点1
[axis addLineToPoint:CGPointMake(chartX, chartY+chartHeight)];//点2
[axis addLineToPoint:CGPointMake(chartX+chartWidth, chartY+chartHeight)];//点3
[COLOR_RGB(200, 200, 200) set];//折线色值
[axis stroke];//画线
注意:UIBezierPath的绘制要在重写父类的方法- (void)drawRect:(CGRect)rect 中进行。如果不能再这里写,需要借助于CAShareLayer来做。这里举个栗子:
// 绘制坐标轴
UIBezierPath *axis = [[UIBezierPath alloc] init];
[axis moveToPoint:CGPointMake(chartX, chartY)];
[axis addLineToPoint:CGPointMake(chartX, chartY+chartHeight)];
[axis addLineToPoint:CGPointMake(chartX+chartWidth, chartY+chartHeight)];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.strokeColor = COLOR_RGB(200, 200, 200).CGColor;
layer.fillColor = [UIColor clearColor].CGColor;
layer.path = axis;
[self.layer addSublayer:layer];
(3)折线图好了,就可以画坐标轴上的刻度值了。
(4)然后画其他非图部分的控件。注意,控件的添加不能在- (void)drawRect:(CGRect)rect 中写,因为这个方法会调很多次,会导致重复创建控件。
(5)最后可以绘制折线。一般折线要根据传入的值来绘制,这里我随便弄了些随机数绘制折线。建议大家,弄些假数据一步步来,总体草图画出来后,效果实现了,再去优化代码,增加可配性,以及数据可变性。
3.代码粘贴
这里的代码只是随便画的草图,效果上述已展示。能画出草图,再去调优就简单多了。
该草图使用方法:
LineChartView *view = [LineChartView new];
view.backgroundColor = [UIColor whiteColor];
view.frame = CGRectMake(0, 100, self.view.bounds.size.width, LineChartViewHeight);
[self.view addSubview:view];
草图代码如下:
LineChartView.h
#import <UIKit/UIKit.h>
#define LCSCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define LCSCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
#define LineChartViewHeight (LCSCREEN_WIDTH/540*456)
@interface LineChartView : UIView
@end
LineChartView.m
#import "LineChartView.h"
#define COLOR_RGB(r,g,b) [UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:1.0]
@interface LineChartView ()
{
CGFloat chartX;
CGFloat chartY;
CGFloat chartWidth;
CGFloat chartHeight;
CGFloat popoverWidth;
CGFloat popoverHeight;
}
@property (nonatomic, strong) UIView *popoverView;
@property (nonatomic, strong) UILabel *popoverLbl;
@property (nonatomic, strong) UIView *currLineView;
@end
@implementation LineChartView
- (instancetype)init
{
self = [super init];
if (self) {
self.backgroundColor = [UIColor whiteColor];
chartX = LCSCREEN_WIDTH/375*60;
chartY = LCSCREEN_WIDTH/375*108;
chartWidth = LCSCREEN_WIDTH-chartX-15.0;
chartHeight = LineChartViewHeight - LCSCREEN_WIDTH/375*152;
popoverWidth = 100;
popoverHeight = 60;
UILabel *titleLbl = [UILabel new];
titleLbl.frame = CGRectMake(10.0, 15.0, 200, 30.0);
titleLbl.textColor = [UIColor blackColor];
titleLbl.font = [UIFont systemFontOfSize:18.0];
titleLbl.text = @"工况发布趋势图";
[self addSubview:titleLbl];
CGFloat itemWidth = 50.0;
for (int i=0; i<4; i++) {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(LCSCREEN_WIDTH-(itemWidth+5.0)*(i+1), 15.0, itemWidth, 30.0);
[btn setTitle:@"日" forState:UIControlStateNormal];
btn.backgroundColor = [UIColor lightGrayColor];
btn.layer.cornerRadius = 15.0;
btn.layer.masksToBounds = YES;
[self addSubview:btn];
}
NSArray *colorArr = @[[UIColor redColor], [UIColor cyanColor], [UIColor greenColor], [UIColor blueColor]];
for (int i=0; i<colorArr.count; i++) {
UIView *view = [UIView new];
view.frame = CGRectMake(LCSCREEN_WIDTH-(itemWidth+5.0)*(i+1), 60.0, itemWidth, 30.0);
[self addSubview:view];
UIView *point = [UIView new];
point.backgroundColor = colorArr[i];
point.frame = CGRectMake(0, 10, 10, 10);
point.layer.cornerRadius = 5;
point.layer.masksToBounds = YES;
[view addSubview:point];
UILabel *lab = [UILabel new];
lab.frame = CGRectMake(15, 0, itemWidth-15, 30);
lab.text = @"文明";
lab.textColor = [UIColor lightGrayColor];
lab.font = [UIFont systemFontOfSize:14];
[view addSubview:lab];
}
}
return self;
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
UITouch *touch = [[touches allObjects] firstObject];
CGFloat currX = [touch locationInView:self].x - chartX;
// 可触控范围
CGFloat canMinX = 0;
CGFloat canMaxX = chartWidth;
if (currX < canMinX || currX > canMaxX) {
return;//超出范围
}
int i = roundf(currX*(30)/canMaxX);//四舍五入
NSLog(@"------%d", i);
self.popoverLbl.text = [NSString stringWithFormat:@"%d数据", i];
// 调整popover和线的位置
CGFloat popoverX = chartX+currX-popoverWidth/2;
if (popoverX > LCSCREEN_WIDTH-popoverWidth) {
popoverX = LCSCREEN_WIDTH-popoverWidth;
}
self.popoverView.hidden = NO;
CGRect rect = self.popoverView.frame;
rect.origin = CGPointMake(popoverX, chartY-10-popoverHeight);
self.popoverView.frame = rect;
CGFloat popoverLineX = chartX+currX;
if (popoverLineX > LCSCREEN_WIDTH-15.0) {
popoverLineX = LCSCREEN_WIDTH-15.0;
}
self.currLineView.hidden = NO;
CGRect rect1 = self.currLineView.frame;
rect1.origin = CGPointMake(popoverLineX, chartY-10);
self.currLineView.frame = rect1;
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
self.popoverView.hidden = YES;
self.currLineView.hidden = YES;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// 绘制坐标轴
UIBezierPath *axis = [[UIBezierPath alloc] init];
[axis moveToPoint:CGPointMake(chartX, chartY)];
[axis addLineToPoint:CGPointMake(chartX, chartY+chartHeight)];
[axis addLineToPoint:CGPointMake(chartX+chartWidth, chartY+chartHeight)];
[COLOR_RGB(200, 200, 200) set];//COLOR_RGB(239, 244, 246)
[axis stroke];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.strokeColor = COLOR_RGB(200, 200, 200).CGColor;
layer.fillColor = [UIColor clearColor].CGColor;
layer.path = axis;
[self.layer addSublayer:layer];
// 绘制Y坐标值
NSArray *arr = @[@(0), @(50), @(150), @(250), @(350), @(450)];
CGFloat YItemHeight = chartHeight/6;
for (int i=1; i<=6; i++) {
UILabel *lab = [UILabel new];
lab.textColor = [UIColor grayColor];
lab.font = [UIFont systemFontOfSize:14];
lab.textAlignment = NSTextAlignmentRight;
lab.frame = CGRectMake(15.0, chartY+chartHeight-YItemHeight*i, chartX-20.0, YItemHeight);
lab.text = [NSString stringWithFormat:@"%@", arr[i-1]];
[self addSubview:lab];
}
// 绘制X坐标值
UILabel *blab = [UILabel new];
blab.frame = CGRectMake(chartX, chartY+chartHeight+5, 100, 30.0);
blab.textColor = [UIColor grayColor];
blab.font = [UIFont systemFontOfSize:14];
blab.text = @"2015-11-01";
[self addSubview:blab];
UILabel *elab = [UILabel new];
elab.frame = CGRectMake(chartX+chartWidth-100, chartY+chartHeight+5, 100, 30.0);
elab.textColor = [UIColor grayColor];
elab.font = [UIFont systemFontOfSize:14];
elab.textAlignment = NSTextAlignmentRight;
elab.text = @"2018-11-30";
[self addSubview:elab];
// 绘制虚线
for (int i=0; i<6; i++) {
CGFloat lineY = chartY+YItemHeight/2+YItemHeight*i;
UIBezierPath *line = [UIBezierPath bezierPath];
[line moveToPoint:CGPointMake(chartX, lineY)];
[line addLineToPoint:CGPointMake(chartX+chartWidth, lineY)];
[line setLineWidth:1];
CGFloat dashPattern[] = {2,1};// 3实线,1空白
[line setLineDash:dashPattern count:1 phase:1];
[COLOR_RGB(239, 244, 246) setStroke];
[line stroke];
}
// 绘制数据线
NSArray<UIColor *> *colorArr = @[[UIColor redColor], [UIColor cyanColor], [UIColor greenColor], [UIColor blueColor]];
for (int i=0; i<colorArr.count; i++) {
UIBezierPath *line = [UIBezierPath bezierPath];
for (int j=0; j<=30; j++) {
CGFloat offsetX = chartWidth/30*j;
CGFloat randomNum = 80*(i+1)+arc4random()%80;
CGFloat offsetY = chartHeight-(randomNum)*chartHeight/450;
if (j==0) {
[line moveToPoint:CGPointMake(chartX, chartY+offsetY)];
} else {
[line addLineToPoint:CGPointMake(chartX+offsetX, chartY+offsetY)];
}
}
[line setLineWidth:1];
[colorArr[i] set];
[line stroke];
}
}
- (UIView *)popoverView {
if (!_popoverView) {
_popoverView = [UIView new];
_popoverView.frame = CGRectMake(0, 0, popoverWidth, popoverHeight);
_popoverView.backgroundColor = [UIColor grayColor];
[self addSubview:_popoverView];
UILabel *lab = [UILabel new];
lab.frame = _popoverView.frame;
lab.textAlignment = NSTextAlignmentCenter;
lab.textColor = [UIColor whiteColor];
[_popoverView addSubview:lab];
_popoverLbl = lab;
}
return _popoverView;
}
- (UIView *)currLineView {
if (!_currLineView) {
_currLineView = [UIView new];
_currLineView.frame = CGRectMake(0, 0, 1.0, chartHeight+10.0);
_currLineView.backgroundColor = [UIColor grayColor];
[self addSubview:_currLineView];
}
return _currLineView;
}
@end