zoukankan      html  css  js  c++  java
  • 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,自己进行拓展

  • 相关阅读:
    Python入门11 —— 基本数据类型的操作
    Win10安装7 —— 系统的优化
    Win10安装6 —— 系统的激活
    Win10安装5 —— 系统安装步骤
    Win10安装4 —— 通过BIOS进入PE
    Win10安装2 —— 版本的选择与下载
    Win10安装1 —— 引言与目录
    Win10安装3 —— U盘启动工具安装
    虚拟机 —— VMware Workstation15安装教程
    Python入门10 —— for循环
  • 原文地址:https://www.cnblogs.com/qqcc1388/p/7359545.html
Copyright © 2011-2022 走看看