iOS 折线图实现

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

话不多说,效果图和代码

//单根折线实现

#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,自己进行拓展

原文地址:https://www.cnblogs.com/qqcc1388/p/7359545.html