做项目要统计商品的销售情况,美工那边给了效果图,自己就按照效果图自定义了一个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
效果: