IOS折线统计图

做项目要统计商品的销售情况,美工那边给了效果图,自己就按照效果图自定义了一个ScrollView。整体效果不错,在做的过程中遇到的问题也记录一下。


现在这个还有许多优化的地方:

1.一个表中只能画一个折线

2.目前的要求最小值为0,没考虑负数的最小值

//
//  LineChartView.h
//  CAShapeLayerTest
//
//  Created by Mkil on 3/31/16.
//  Copyright © 2016 Mkil. All rights reserved.
//

#import <UIKit/UIKit.h>



#define SPACING 70.0        //设置两点的水平间距
#define BOTTOMSPACING 60.0  //底部距离
#define LEFTSPACING 80.0   //左边距离

//文本类型
typedef NS_ENUM(NSInteger, TitleType) {
    TitleForX,
    TitleForPoint,
    TitelForNormal
};

@class LineChartView;

@protocol LineChartDataSource  <NSObject>
@required

//折点数量
- (NSInteger)numberForChart:(LineChartView *)chart;

//折点数值
- (float)chart:(LineChartView *)chart valueAtIndex:(NSInteger)index;

@optional

//X轴坐标是否有背景色
-(BOOL)chart:(LineChartView *)chart backGroundColorAtXPointIndex:(NSInteger)index;

//X 轴的标题
-(NSString *)chart:(LineChartView *)chart titleForXLabelAtIndex:(NSInteger)index;


@end

@interface LineChartView : UIScrollView

@property(nonatomic, assign)id<LineChartDataSource> dataSource;
@property (nonatomic, assign) NSInteger maxValue;

@end
//
//  LineChartView.m
//  CAShapeLayerTest
//
//  Created by Mkil on 3/31/16.
//  Copyright © 2016 Mkil. All rights reserved.
//

#import "LineChartView.h"

#define textDColor [UIColor colorWithRed:255/255.0 green:164/255.0 blue:102/255.0 alpha:1]
#define line228Color [UIColor colorWithRed:228/255.0 green:228/255.0 blue:228/255.0 alpha:1]

@interface  LineChartView ()

@property (nonatomic, strong) CAShapeLayer *linePath;
@property (nonatomic, assign) NSInteger count;          //点数
@property (nonatomic, assign) CGFloat avgHeight;        //刻度

@end

@implementation LineChartView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor whiteColor];
        _linePath = [CAShapeLayer layer];
        _linePath.lineCap = kCALineCapRound;
        _linePath.lineJoin = kCALineJoinBevel;
        _linePath.lineWidth = 2;
        _linePath.fillColor = [UIColor clearColor].CGColor;
        
        self.bounces=NO;
//        self.backgroundColor = [UIColor lightGrayColor];
        self.showsHorizontalScrollIndicator=NO;
        self.showsVerticalScrollIndicator=NO;
    }
    return self;
}

//点数
-(NSInteger)count
{
    return [_dataSource numberForChart:self];
}

//刻度
-(CGFloat)avgHeight
{
    CGFloat height=self.frame.size.height;
    _avgHeight=(height-40-20)/self.maxValue;
    return _avgHeight;
}

- (void)drawRect:(CGRect)rect
{
    
    [super drawRect:rect];
    
    NSInteger count = [self.layer.sublayers count];
    for (int i = 0; i < count ; i ++) {
        
        [self.layer.sublayers[i] removeFromSuperlayer];
    }
    
    //不能通过for in 删除  was mutated while being enumerated 遍历的时候不能删除
    //    for (CALayer *layer in self.layer.sublayers) {
    //        [layer removeFromSuperlayer];
    //    }
    
    if (self.count <= 0) {
        
        return;
    }
    
    [self.layer addSublayer:_linePath];
     self.contentSize=CGSizeMake((self.count)*SPACING + LEFTSPACING, self.bounds.size.height);
   
    //画横线
    [self drawTransverseLineHeight:self.frame.size.height - 40];
    [self drawTransverseLineHeight:20];
    [self drawTransverseLineHeight:20 + (self.frame.size.height - 40 - 20)/2.0];
    
    //画文字
    CGPoint point = CGPointMake(40, 20);
    
    [self drawText:[NSString stringWithFormat:@"%ld",self.maxValue] withPoint:point withType:TitelForNormal withColor:line228Color withIndex:1];
    point = CGPointMake(40, 20 + (self.frame.size.height - 40 - 20)/2.0);
    [self drawText:[NSString stringWithFormat:@"%ld",self.maxValue/2] withPoint:point withType:TitelForNormal withColor:line228Color withIndex:1];
    //画折线
    [self drawBrokenLine];
    
}

//画横线
- (void)drawTransverseLineHeight:(CGFloat) height
{
    CAShapeLayer *layer=[CAShapeLayer layer];
    layer.lineCap=kCALineCapRound;
    layer.lineJoin=kCALineJoinBevel;
    layer.lineWidth=0.1;
    layer.fillColor=[UIColor clearColor].CGColor;
    [self.layer addSublayer:layer];
    
    UIBezierPath *transverseLine = [UIBezierPath bezierPath];
    [transverseLine moveToPoint:CGPointMake(20, height)];
    [transverseLine addLineToPoint:CGPointMake(self.contentSize.width - 20, height)];
    
    layer.path = transverseLine.CGPath;
    layer.strokeColor = [UIColor blackColor].CGColor;
}

//画竖线
-(void)drawVerticalLineStartPoint:(CGPoint) startPoint withEndPoint:(CGPoint) endPoint withColor:(UIColor *)color
{
    CAShapeLayer *layer=[CAShapeLayer layer];
    layer.lineCap=kCALineCapRound;
    layer.lineJoin=kCALineJoinBevel;
    layer.lineWidth=0.5;
    layer.fillColor=[UIColor clearColor].CGColor;
    [self.layer addSublayer:layer];
    
    UIBezierPath *bottomLinePath=[UIBezierPath bezierPath];
    [bottomLinePath moveToPoint:startPoint];
    [bottomLinePath addLineToPoint:endPoint];
    layer.path=bottomLinePath.CGPath;
    layer.strokeColor=[UIColor colorWithRed:0.902f green:0.902f blue:0.902f alpha:1.00f].CGColor;
}

//画文字

-(void)drawText:(NSString *)text withPoint:(CGPoint)point withType:(TitleType) type withColor:(UIColor *)textColor withIndex:(NSInteger)index
{
    
    CGRect frame=[text boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:10.f]} context:nil];
    
    UILabel *label=[[UILabel alloc]init];
    
    CGPoint pointForValueString=CGPointMake(point.x-15, point.y);
    if (pointForValueString.y+frame.size.height>self.frame.size.height-50) {
        pointForValueString.y=point.y-frame.size.height-20;
    }
    else if (pointForValueString.y-frame.size.height<0)
    {
        pointForValueString.y=point.y+frame.size.height-20;
    }
    label.frame=CGRectMake(pointForValueString.x - 15, pointForValueString.y, 60, 30);
    label.textAlignment=NSTextAlignmentCenter;
    if (type==TitleForPoint) {
        label.textColor=textColor;
        label.font=[UIFont systemFontOfSize:13.f];
    }
    else if (type==TitleForX)
    {
        label.frame=CGRectMake(pointForValueString.x, pointForValueString.y+15, 30,30);
        if (_dataSource&&[_dataSource respondsToSelector:@selector(chart:backGroundColorAtXPointIndex:)]) {
            if ([_dataSource chart:self backGroundColorAtXPointIndex:index]) {
                label.backgroundColor=textColor;
                label.textColor=[UIColor whiteColor];
                label.layer.cornerRadius=label.bounds.size.width/2;
                label.clipsToBounds=YES;
            }
            else
            {
                label.backgroundColor=[UIColor whiteColor];
                label.textColor=[UIColor colorWithRed:0.298f green:0.298f blue:0.298f alpha:1.00f];
                
            }
            
        }
        else
        {
            label.textColor=[UIColor colorWithRed:0.298f green:0.298f blue:0.298f alpha:1.00f];
        }
        label.font=[UIFont systemFontOfSize:14.f];
    }else if (type == TitelForNormal)
    {
        label.textColor=textColor;
        label.font=[UIFont systemFontOfSize:18.f];
    }
    label.text=text;
    [self addSubview:label];
}

- (void)drawBrokenLine
{
    UIBezierPath *path = [UIBezierPath bezierPath];
    for (int i=0; i < self.count; i++) {
        
        CGFloat value = [_dataSource chart:self valueAtIndex:i];
        CGPoint point = [self pointWithValue:value index:i];
    
        //画竖线
        if (i == 0) {
            
            [self drawVerticalLineStartPoint:CGPointMake(LEFTSPACING, self.frame.size.height-40) withEndPoint:CGPointMake(LEFTSPACING, self.frame.size.height - 30) withColor:
             [UIColor blueColor]];
            
            [self drawVerticalLineStartPoint:CGPointMake(LEFTSPACING, self.frame.size.height-40) withEndPoint:point withColor:
             [UIColor blueColor]];
        }else{
            
            [self drawVerticalLineStartPoint:CGPointMake(i*SPACING + LEFTSPACING, self.frame.size.height-40) withEndPoint:CGPointMake(i*SPACING + LEFTSPACING, self.frame.size.height - 30) withColor:
             [UIColor blueColor]];
            
            [self drawVerticalLineStartPoint:CGPointMake(i*SPACING + LEFTSPACING, self.frame.size.height-40) withEndPoint:point withColor:
             [UIColor blueColor]];
        }
        
        //画文字
        NSString *valueString=[NSString stringWithFormat:@"%ld",(long)value];
        [self drawText:valueString withPoint:point withType:TitleForPoint withColor:textDColor withIndex:i];
        //画X轴
        if (_dataSource&&[_dataSource respondsToSelector:@selector(chart:titleForXLabelAtIndex:)]) {
            NSString *xstring=[_dataSource chart:self titleForXLabelAtIndex:i];
            [self drawText:xstring withPoint:CGPointMake(point.x, self.bounds.size.height-10) withType:TitleForX withColor:line228Color withIndex:i];
        }
        
        //画折线
        //贝塞尔曲线
        if (i==0) {
            [path moveToPoint:point];
        }else{
            [path addLineToPoint:point];
        }

    }
    
    path.lineCapStyle = kCGLineCapRound;
    path.lineJoinStyle=kCGLineJoinRound;
    path.lineWidth=0.5;
    [[UIColor redColor]setStroke];
    CABasicAnimation *pathAnimation=[CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    pathAnimation.duration = 2;
    pathAnimation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    pathAnimation.fromValue=[NSNumber numberWithFloat:0.0f];
    pathAnimation.toValue=[NSNumber numberWithFloat:1.0f];
    pathAnimation.autoreverses=NO;
    _linePath.path=path.CGPath;
    _linePath.strokeColor=textDColor.CGColor;
    [_linePath addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
    _linePath.strokeEnd = 1.0;

}


//根据索引计算出折点的位置
-(CGPoint)pointWithValue:(NSInteger)value index:(NSInteger)index
{
    CGFloat height=self.frame.size.height;
    
    if (index == 0) {
        
        return CGPointMake(LEFTSPACING, height-value*self.avgHeight-40);
    }
    return  CGPointMake(index*SPACING + LEFTSPACING, height-value*self.avgHeight-40);
}

@end

调用:

//
//  ViewController.m
//  CAShapeLayerTest
//
//  Created by Mkil on 3/31/16.
//  Copyright © 2016 Mkil. All rights reserved.
//

#import "ViewController.h"
#import "LineChartView.h"

@interface ViewController ()<LineChartDataSource>
@property (nonatomic ,strong) NSArray *points;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    _points=@[@3000,@400,@2500,@3500,@1800,@2800,@900,@3400,@2900,@3100,@3000,@2500];
    
    LineChartView *lineChart=[[LineChartView alloc]initWithFrame:CGRectMake(0, 100, self.view.bounds.size.width, 300)];
    
    lineChart.dataSource=self;
    lineChart.maxValue = 4000;
    
    [self.view addSubview:lineChart];
    
}


-(NSInteger)numberForChart:(LineChartView *)chart
{
    return _points.count;
}
-(float)chart:(LineChartView *)chart valueAtIndex:(NSInteger)index
{
    
    return [[_points objectAtIndex:index] floatValue];
}

-(NSString *)chart:(LineChartView *)chart titleForXLabelAtIndex:(NSInteger)index
{
    return [NSString stringWithFormat:@"%ld",index*2];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

效果:




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值