iOS 折线图实现

图表绘制的过程实际上是坐标位置的计算过程,至于画线只要有了position,通过CAShapeLayer+BezierPath很快就可以画出来,这里提供一个绘制折线的demo,贵在思路,有需要的可以参考

demo下载地址:https://github.com/qqcc1388/TYLineViewDemo

话不多说,效果图和代码

iOS 折线图实现

//单根折线实现

#import <UIKit/UIKit.h>

@interface TYLineView : UIView

@property (nonatomic,strong) NSArray *datas;

@property (nonatomic,strong) UIColor *lineColor;

/**
是否显示灰色背景
*/
@property (nonatomic,assign) BOOL isShowBack; @end #import "TYLineView.h" #define kMarginX 30
#define kMarginY 20 @interface TYLineView () @property (nonatomic,strong)CAShapeLayer *shapeLayer;//划线layer @property (nonatomic,strong) CAShapeLayer *backLayer; //背景 @property (nonatomic,assign)CGFloat maxYvalue; //最大y值 @property (nonatomic,assign) NSInteger xAxisCount; //x轴点数 @property (nonatomic,assign) NSInteger yAxisCount; //y轴点数 @end @implementation TYLineView -(instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) { [self initialize];
}
return self;
} -(instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) { [self initialize];
}
return self;
} -(void)initialize
{
//闭合背景
_backLayer = [[CAShapeLayer alloc] init];
_backLayer.fillColor = [UIColor grayColor].CGColor;
_backLayer.frame = self.bounds;
[self.layer addSublayer:_backLayer]; //主线段
_shapeLayer = [[CAShapeLayer alloc] init];
_shapeLayer.lineWidth = 1;
_shapeLayer.lineCap = @"round";
_shapeLayer.lineJoin = @"round";
_shapeLayer.strokeColor = [UIColor redColor].CGColor;
_shapeLayer.fillColor = [UIColor clearColor].CGColor;
_shapeLayer.frame = self.bounds;
[self.layer addSublayer:_shapeLayer]; //初始化
self.isShowBack = NO;
self.yAxisCount = 5;
self.backgroundColor = [UIColor cyanColor];
} -(void)setDatas:(NSArray *)datas{
//保存数据
_datas = datas; //设置最大值
self.maxYvalue = 200;
//设置xAxisCount
self.xAxisCount = datas.count; [self setNeedsDisplay];
//划线
[self drawLine];
} -(void)drawLine
{ CGFloat totalHeight = CGRectGetHeight(self.frame) - kMarginY*2;
// CGFloat maxY = self.maxYvalue;
CGFloat totoalWidth = CGRectGetWidth(self.frame) - kMarginX*2;
//x轴每一段的宽度
CGFloat perX = totoalWidth/(self.xAxisCount-1)*1.0; CGFloat yper = totalHeight/self.maxYvalue; //y轴一个单位的高度
//主线段曲线
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
//背景曲线
UIBezierPath *backPath = [UIBezierPath bezierPath];
//原点
CGPoint startPoint = CGPointMake(kMarginX,totalHeight + kMarginY);
[backPath moveToPoint:startPoint]; for (int i = 0; i < _datas.count; i++) {
NSInteger valueY = [_datas[i] integerValue];
CGFloat x = kMarginX + perX*i;
CGFloat y = (totalHeight + kMarginY) - yper*valueY;
CGPoint point = CGPointMake(x,y);
if (i == 0) {
[bezierPath moveToPoint:point];
}else{
[bezierPath addLineToPoint:point];
}
[backPath addLineToPoint:point];
}
//终点
CGPoint endPoint = CGPointMake(kMarginX + perX*(self.datas.count-1), totalHeight + kMarginY);
[backPath addLineToPoint:endPoint]; //开始动画
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.duration = 2.0f;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.fromValue = @(0);
animation.toValue =@(1);
self.shapeLayer.path = bezierPath.CGPath;
[self.shapeLayer addAnimation:animation forKey:@"strokeEnd"];
self.backLayer.path = backPath.CGPath;
} -(void)drawRect:(CGRect)rect
{
[super drawRect:rect];
UIBezierPath *path = [UIBezierPath bezierPath];
[path setLineWidth:1.0f];
[[UIColor redColor] set]; CGFloat totalWidth = self.bounds.size.width;
CGFloat totalHeight = self.bounds.size.height; //画坐标系
//------> y轴
[path moveToPoint:CGPointMake(kMarginX,kMarginY)];
[path addLineToPoint:CGPointMake(kMarginX, totalHeight - kMarginY)]; //------> x轴
[path addLineToPoint:CGPointMake(totalWidth - kMarginX, totalHeight - kMarginY)];
[path stroke]; //线段 - y轴
CGFloat perHeight = ((totalHeight - kMarginY*2)/(self.yAxisCount));
for (int i = 0; i < self.yAxisCount; i++) {
CGFloat y = perHeight*i + kMarginY; UIBezierPath *path = [UIBezierPath bezierPath];
[path setLineWidth:1.0f];
[[UIColor blueColor] set];
[path moveToPoint:CGPointMake(kMarginX, y)];
[path addLineToPoint:CGPointMake(kMarginX+ 5, y)];
[path stroke];
} //线段 - x轴
CGFloat perWidth = (totalWidth - kMarginX*2)/(self.xAxisCount*1.0);
for (int i = 0; i < self.xAxisCount; i++) {
CGFloat x = perWidth*(i+1);
CGFloat y = totalHeight - kMarginY;
UIBezierPath *path = [UIBezierPath bezierPath];
[path setLineWidth:1.0f];
[[UIColor blueColor] set];
[path moveToPoint:CGPointMake(x+kMarginX, y)];
[path addLineToPoint:CGPointMake(x+kMarginX, y-5)];
[path stroke];
} //画y轴文字
NSMutableArray *yArr = [NSMutableArray array]; for (int i = 0; i < self.yAxisCount; i++) { [yArr addObject:[NSString stringWithFormat:@"%.f",self.maxYvalue - self.maxYvalue/self.yAxisCount *i]];
}
[yArr addObject:@"0"]; for (int i = 0; i < yArr.count ; i++) {
NSString *title = yArr[i];
CGFloat y = ((totalHeight - kMarginY*2)/(self.yAxisCount))*i + kMarginY;
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
[style setAlignment:NSTextAlignmentCenter]; [title drawInRect:CGRectMake(0,y-5, kMarginX, 20) withAttributes:@{NSForegroundColorAttributeName:[UIColor redColor],NSFontAttributeName:[UIFont systemFontOfSize:10],NSParagraphStyleAttributeName:style}];
} } #pragma mark setter getter
-(void)setLineColor:(UIColor *)lineColor{
_lineColor = lineColor;
self.shapeLayer.strokeColor = lineColor.CGColor;
} -(void)setIsShowBack:(BOOL)isShowBack{
_isShowBack = isShowBack;
self.backLayer.hidden = !isShowBack;
}

多根线一起

#import <UIKit/UIKit.h>

@interface TYMultiLineView : UIView

-(void)addLineWithDatas:(NSArray *)datas lineColor:(UIColor *)color animated:(BOOL)animated;

@end

#import "TYMultiLineView.h"

#define kMarginX        30
#define kMarginY 20 @interface TYMultiLineView () @property (nonatomic,assign)CGFloat maxYvalue; //最大y值 @property (nonatomic,assign) NSInteger xAxisCount; //x轴点数 @property (nonatomic,assign) NSInteger yAxisCount; //y轴点数 @end @implementation TYMultiLineView -(instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) { [self initialize];
}
return self;
} -(instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) { [self initialize];
}
return self;
} -(void)initialize
{
self.backgroundColor = [UIColor cyanColor]; self.maxYvalue = 200;
self.yAxisCount = 5;
self.xAxisCount = 5; [self setNeedsDisplay];
} -(void)addLineWithDatas:(NSArray *)datas lineColor:(UIColor *)color animated:(BOOL)animated{ //设置最大值
self.maxYvalue = 200;
//设置xAxisCount
self.xAxisCount = datas.count; CAShapeLayer* lineLayer = [[CAShapeLayer alloc] init];
lineLayer.lineWidth = 1;
lineLayer.lineCap = @"round";
lineLayer.lineJoin = @"round";
lineLayer.strokeColor = color.CGColor;
lineLayer.fillColor = [UIColor clearColor].CGColor;
lineLayer.frame = self.bounds;
[self.layer addSublayer:lineLayer]; UIBezierPath *path = [self prepareBezierPathDatas:datas]; lineLayer.path = path.CGPath; if(animated){
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.duration = 2.0f;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.fromValue = @(0);
animation.toValue =@(1);
[lineLayer addAnimation:animation forKey:@"strokeEnd"];
} } -(UIBezierPath *)prepareBezierPathDatas:(NSArray *)datas
{ CGFloat totalHeight = CGRectGetHeight(self.frame) - kMarginY*2;
// CGFloat maxY = self.maxYvalue;
CGFloat totoalWidth = CGRectGetWidth(self.frame) - kMarginX*2;
//x轴每一段的宽度
CGFloat perX = totoalWidth/(self.xAxisCount-1)*1.0; CGFloat yper = totalHeight/self.maxYvalue; //y轴一个单位的高度
//主线段曲线
UIBezierPath *bezierPath = [UIBezierPath bezierPath]; for (int i = 0; i < datas.count; i++) {
NSInteger valueY = [datas[i] integerValue];
CGFloat x = kMarginX + perX*i;
CGFloat y = (totalHeight + kMarginY) - yper*valueY;
CGPoint point = CGPointMake(x,y);
if (i == 0) {
[bezierPath moveToPoint:point];
}else{
[bezierPath addLineToPoint:point];
}
}
return bezierPath;
} -(void)drawRect:(CGRect)rect
{
[super drawRect:rect];
UIBezierPath *path = [UIBezierPath bezierPath];
[path setLineWidth:1.0f];
[[UIColor redColor] set]; CGFloat totalWidth = self.bounds.size.width;
CGFloat totalHeight = self.bounds.size.height; //画坐标系
//------> y轴
[path moveToPoint:CGPointMake(kMarginX,kMarginY)];
[path addLineToPoint:CGPointMake(kMarginX, totalHeight - kMarginY)]; //------> x轴
[path addLineToPoint:CGPointMake(totalWidth - kMarginX, totalHeight - kMarginY)];
[path stroke]; //线段 - y轴
CGFloat perHeight = ((totalHeight - kMarginY*2)/(self.yAxisCount));
for (int i = 0; i < self.yAxisCount; i++) {
CGFloat y = perHeight*i + kMarginY; UIBezierPath *path = [UIBezierPath bezierPath];
[path setLineWidth:1.0f];
[[UIColor blueColor] set];
[path moveToPoint:CGPointMake(kMarginX, y)];
[path addLineToPoint:CGPointMake(kMarginX+ 5, y)];
[path stroke];
} //线段 - x轴
CGFloat perWidth = (totalWidth - kMarginX*2)/(self.xAxisCount*1.0);
for (int i = 0; i < self.xAxisCount; i++) {
CGFloat x = perWidth*(i+1);
CGFloat y = totalHeight - kMarginY;
UIBezierPath *path = [UIBezierPath bezierPath];
[path setLineWidth:1.0f];
[[UIColor blueColor] set];
[path moveToPoint:CGPointMake(x+kMarginX, y)];
[path addLineToPoint:CGPointMake(x+kMarginX, y-5)];
[path stroke];
} //画y轴文字
NSMutableArray *yArr = [NSMutableArray array]; for (int i = 0; i < self.yAxisCount; i++) { [yArr addObject:[NSString stringWithFormat:@"%.f",self.maxYvalue - self.maxYvalue/self.yAxisCount *i]];
}
[yArr addObject:@"0"]; for (int i = 0; i < yArr.count ; i++) {
NSString *title = yArr[i];
CGFloat y = ((totalHeight - kMarginY*2)/(self.yAxisCount))*i + kMarginY;
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
[style setAlignment:NSTextAlignmentCenter]; [title drawInRect:CGRectMake(0,y-5, kMarginX, 20) withAttributes:@{NSForegroundColorAttributeName:[UIColor redColor],NSFontAttributeName:[UIFont systemFontOfSize:10],NSParagraphStyleAttributeName:style}];
} }

使用方法

lineView:
[self.lineView setDatas:[self prepareDatas]]; multiLineView:
[self.multiLineView addLineWithDatas:[self prepareDatas] lineColor:[UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1] animated:YES]; -(NSArray *)prepareDatas{
NSMutableArray *datas = [NSMutableArray array];
for (int i = 0; i < 5; i++) {
[datas addObject:@(arc4random_uniform(201)).stringValue];
}
return datas;
}

关于动画画线方法:给shapelayer添加strokeEnd动画

        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.duration = 2.0f;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.fromValue = @(0);
animation.toValue =@(1);
[lineLayer addAnimation:animation forKey:@"strokeEnd"];

demo中还有很多地方需要完善,这里仅仅是抛砖引玉提供一种实现的方案,细节的处理和其他的特殊需求请参考demo,自己进行拓展

上一篇:window下mysql创建库只读


下一篇:python系统编程(八)