You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
289 lines
12 KiB
289 lines
12 KiB
// |
|
// ZHLineChartView.m |
|
// ZHLineChart |
|
// |
|
// Created by 周亚楠 on 2020/3/1. |
|
// Copyright © 2020 Zhou. All rights reserved. |
|
// |
|
|
|
#import "ZHLineChartView.h" |
|
#import "UIBezierPath+ThroughPointsBezier.h" |
|
|
|
@interface ZHLineChartView () |
|
@property (nonatomic, strong) NSMutableArray *pointArr; |
|
@end |
|
|
|
@implementation ZHLineChartView |
|
|
|
- (instancetype)initWithFrame:(CGRect)frame |
|
{ |
|
self = [super initWithFrame:frame]; |
|
if (self) { |
|
self.backgroundColor = [UIColor whiteColor]; |
|
|
|
self.circleRadius = 3.f; |
|
self.lineWidth = 1.5f; |
|
self.horizontalLineWidth = 0.5f; |
|
self.horizontalBottomLineWidth = 1.f; |
|
self.dataTextWidth = 20; |
|
self.leftTextWidth = 25; |
|
self.scaleOffset = 0.f; |
|
self.showCalibration = YES; |
|
self.bottomOffset = 20.f; |
|
self.lineToLeftOffset = 5; |
|
self.angle = M_PI * 1.75; |
|
self.textFontSize = 10; |
|
self.edge = UIEdgeInsetsMake(25, 5, 40, 15); |
|
|
|
self.showKeyPoint = YES; |
|
self.circleStrokeColor = KLineColor; |
|
self.circleFillColor = [UIColor whiteColor]; |
|
self.textColor = KTextColor; |
|
self.lineColor = KLineColor; |
|
self.horizontalLineColor = KHorizontalLineColor; |
|
self.horizontalBottomLineColor = KHorizontalBottomLineColor; |
|
|
|
self.addCurve = YES; |
|
self.toCenter = YES; |
|
self.supplement = NO; |
|
self.showLineData = YES; |
|
self.showColorGradient = YES; |
|
|
|
self.showHorizontalLine = YES; |
|
self.horizontalLineStyle = 0; |
|
self.showVerticalLine = YES; |
|
self.verticalLineStyle = 0; |
|
|
|
|
|
self.colorArr = [NSArray arrayWithObjects:(id)[[self.lineColor colorWithAlphaComponent:0.4] CGColor],(id)[[[UIColor whiteColor] colorWithAlphaComponent:0.1] CGColor], nil]; |
|
|
|
} |
|
return self; |
|
} |
|
|
|
|
|
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ |
|
NSLog(@"开始触摸"); |
|
} |
|
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ |
|
UITouch* touch = [touches anyObject]; |
|
CGPoint currentPoint = [touch locationInView:self]; // 当前视图内的坐标 |
|
NSLog(@"移动的点 currentPointX: %f currentPointY: %f", currentPoint.x, currentPoint.y); |
|
} |
|
/** |
|
* 渲染折线图 |
|
*/ |
|
- (void)drawLineChart |
|
{ |
|
NSMutableArray *pointArr = [NSMutableArray array]; |
|
CGFloat labelHeight = self.textFontSize; |
|
NSInteger numSpace = (self.max.integerValue - self.min.integerValue) / self.splitCount; |
|
CGFloat spaceY = (self.frame.size.height - self.edge.top - self.edge.bottom - ((self.splitCount + 1) * labelHeight)) / self.splitCount; |
|
CGFloat minMidY = 0.f; |
|
CGFloat maxMidY; |
|
|
|
for (int i = 0; i < self.splitCount + 1; i ++) { |
|
//创建纵轴文本 |
|
UILabel *leftLabel = [[UILabel alloc] init]; |
|
leftLabel.frame = CGRectMake(self.edge.left, self.leftTextWidth + (spaceY + labelHeight) * i, self.leftTextWidth, labelHeight); |
|
leftLabel.textColor = self.textColor; |
|
leftLabel.textAlignment = NSTextAlignmentRight; |
|
leftLabel.font = [UIFont systemFontOfSize:self.textFontSize]; |
|
NSInteger leftNum = self.max.integerValue - numSpace * i; |
|
if (i == self.splitCount) { |
|
leftNum = self.min.integerValue; |
|
} |
|
leftLabel.text = [NSString stringWithFormat:@"%ld",leftNum]; |
|
[self addSubview:leftLabel]; |
|
|
|
if (!i) { |
|
minMidY = CGRectGetMidY(leftLabel.frame); |
|
} |
|
|
|
UIBezierPath *linePath = [UIBezierPath bezierPath]; |
|
CGFloat minX = CGRectGetMaxX(leftLabel.frame) + self.lineToLeftOffset; |
|
CGFloat maxX = CGRectGetMaxX(self.frame) - self.edge.right; |
|
[linePath moveToPoint:CGPointMake(minX, CGRectGetMidY(leftLabel.frame))]; |
|
[linePath addLineToPoint:CGPointMake(maxX, CGRectGetMidY(leftLabel.frame))]; |
|
|
|
CAShapeLayer *hLineLayer = [CAShapeLayer layer]; |
|
if (i == self.splitCount) { |
|
hLineLayer.strokeColor = self.horizontalBottomLineColor.CGColor; |
|
hLineLayer.lineWidth = self.horizontalBottomLineWidth; |
|
|
|
CGFloat spaceX = (maxX - minX) / (self.horizontalDataArr.count - (self.toCenter ? 0 : 1)); |
|
maxMidY = CGRectGetMidY(leftLabel.frame); |
|
if(self.showCalibration){ //是否需要刻度 |
|
//创建刻度 |
|
UIBezierPath *bezierPath = [UIBezierPath bezierPath]; |
|
NSInteger count = self.horizontalDataArr.count; |
|
if (self.toCenter) { |
|
count = self.horizontalDataArr.count + 1; |
|
} |
|
for (int j = 0 ; j < count; j ++) { |
|
[bezierPath moveToPoint:CGPointMake(minX + spaceX * j, maxMidY + self.scaleOffset)]; |
|
[bezierPath addLineToPoint:CGPointMake(minX + spaceX * j, maxMidY + 2 + self.scaleOffset)]; |
|
[linePath appendPath:bezierPath]; |
|
} |
|
} |
|
//创建横轴文本 |
|
CGFloat bottomLabelWidth = spaceX + 20; |
|
CGFloat ratio = (maxMidY - minMidY) / (self.max.floatValue - self.min.floatValue); |
|
for (int k = 0; k < self.horizontalDataArr.count; k ++) { |
|
CGFloat midX = minX + (spaceX * k) + (self.toCenter ? spaceX / 2 : 0); |
|
UILabel *bottomLabel = [[UILabel alloc] init]; |
|
bottomLabel.frame = CGRectMake(midX - bottomLabelWidth / 2, maxMidY + self.bottomOffset, bottomLabelWidth, labelHeight); |
|
bottomLabel.textColor = self.textColor; |
|
bottomLabel.textAlignment = NSTextAlignmentCenter; |
|
bottomLabel.font = [UIFont systemFontOfSize:self.textFontSize]; |
|
bottomLabel.text = self.horizontalDataArr[k]; |
|
[self addSubview:bottomLabel]; |
|
//旋转 |
|
bottomLabel.transform = CGAffineTransformMakeRotation(self.angle); |
|
|
|
//构造关键点 |
|
NSNumber *tempNum = self.lineDataAry[k]; |
|
CGFloat y = maxMidY - (tempNum.integerValue - self.min.floatValue) * ratio; |
|
if (self.toCenter && self.supplement && !k) { |
|
NSValue *value = [NSValue valueWithCGPoint:CGPointMake(minX, y)]; |
|
[pointArr addObject:value]; |
|
} |
|
NSValue *value = [NSValue valueWithCGPoint:CGPointMake(midX, y)]; |
|
[pointArr addObject:value]; |
|
if (self.toCenter && self.supplement && k == self.lineDataAry.count - 1) { |
|
NSValue *value = [NSValue valueWithCGPoint:CGPointMake(maxX, y)]; |
|
[pointArr addObject:value]; |
|
} |
|
} |
|
//绘制折线 |
|
[self drawLineLayerWithPointArr:pointArr maxMidY:maxMidY]; |
|
|
|
} else { |
|
hLineLayer.strokeColor = self.horizontalLineColor.CGColor; |
|
hLineLayer.lineWidth = self.horizontalLineWidth; |
|
} |
|
hLineLayer.path = linePath.CGPath; |
|
hLineLayer.fillColor = [UIColor clearColor].CGColor; |
|
hLineLayer.lineCap = kCALineCapRound; |
|
hLineLayer.lineJoin = kCALineJoinRound; |
|
hLineLayer.contentsScale = [UIScreen mainScreen].scale; |
|
[self.layer addSublayer:hLineLayer]; |
|
} |
|
} |
|
|
|
/** |
|
* 绘制折线及渐变 |
|
*/ |
|
- (void)drawLineLayerWithPointArr:(NSMutableArray *)pointArr maxMidY:(CGFloat)maxMidY |
|
{ |
|
CGPoint startPoint = [[pointArr firstObject] CGPointValue]; |
|
CGPoint endPoint = [[pointArr lastObject] CGPointValue]; |
|
UIBezierPath *linePath = [UIBezierPath bezierPath]; |
|
[linePath moveToPoint:startPoint]; |
|
if (self.addCurve) { |
|
[linePath addBezierThroughPoints:pointArr]; |
|
} else { |
|
[linePath addNormalBezierThroughPoints:pointArr]; |
|
} |
|
|
|
CAShapeLayer *lineLayer = [CAShapeLayer layer]; |
|
lineLayer.path = linePath.CGPath; |
|
lineLayer.strokeColor = self.lineColor.CGColor; |
|
lineLayer.fillColor = [UIColor clearColor].CGColor; |
|
lineLayer.lineWidth = self.lineWidth; |
|
lineLayer.lineCap = kCALineCapRound; |
|
lineLayer.lineJoin = kCALineJoinRound; |
|
lineLayer.contentsScale = [UIScreen mainScreen].scale; |
|
[self.layer addSublayer:lineLayer]; |
|
|
|
//颜色渐变 |
|
if (self.showColorGradient) { |
|
UIBezierPath *colorPath = [UIBezierPath bezierPath]; |
|
colorPath.lineWidth = 1.f; |
|
[colorPath moveToPoint:startPoint]; |
|
if (self.addCurve) { |
|
[colorPath addBezierThroughPoints:pointArr]; |
|
} else { |
|
[colorPath addNormalBezierThroughPoints:pointArr]; |
|
} |
|
[colorPath addLineToPoint:CGPointMake(endPoint.x, maxMidY)]; |
|
[colorPath addLineToPoint:CGPointMake(startPoint.x, maxMidY)]; |
|
[colorPath addLineToPoint:CGPointMake(startPoint.x, startPoint.y)]; |
|
|
|
CAShapeLayer *bgLayer = [CAShapeLayer layer]; |
|
bgLayer.path = colorPath.CGPath; |
|
bgLayer.frame = self.bounds; |
|
|
|
CAGradientLayer *colorLayer = [CAGradientLayer layer]; |
|
colorLayer.frame = bgLayer.frame; |
|
colorLayer.mask = bgLayer; |
|
colorLayer.startPoint = CGPointMake(0, 0); |
|
colorLayer.endPoint = CGPointMake(0, 1); |
|
colorLayer.colors = self.colorArr; |
|
[self.layer addSublayer:colorLayer]; |
|
} |
|
|
|
|
|
// CABasicAnimation *maxPathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; |
|
// maxPathAnimation.duration = 12; |
|
// maxPathAnimation.repeatCount = 1; |
|
// maxPathAnimation.removedOnCompletion = YES; |
|
// maxPathAnimation.fromValue = @0.f; |
|
// maxPathAnimation.toValue = @1; |
|
// [maxLineLayer addAnimation:maxPathAnimation forKey:@"strokeEnd"]; |
|
//绘制关键折线及关键点 |
|
if(self.showKeyPoint){ |
|
[self buildDotWithPointsArr:pointArr]; |
|
} |
|
|
|
} |
|
|
|
/** |
|
* 绘制关键点及关键点数据展示 |
|
*/ |
|
- (void)buildDotWithPointsArr:(NSMutableArray *)pointsArr |
|
{ |
|
for (int i = 0; i < pointsArr.count; i ++) { |
|
if (self.toCenter && self.supplement && (!i || i == pointsArr.count - 1)) { |
|
continue; |
|
} |
|
NSValue *point = pointsArr[i]; |
|
|
|
//关键点绘制 |
|
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(point.CGPointValue.x, point.CGPointValue.y) radius:self.circleRadius startAngle:0 endAngle:M_PI * 2 clockwise:NO]; |
|
CAShapeLayer *circleLayer = [CAShapeLayer layer]; |
|
circleLayer.path = path.CGPath; |
|
circleLayer.strokeColor = self.circleStrokeColor.CGColor; |
|
circleLayer.fillColor = self.circleFillColor.CGColor; |
|
circleLayer.lineWidth = self.lineWidth; |
|
circleLayer.lineCap = kCALineCapRound; |
|
circleLayer.lineJoin = kCALineJoinRound; |
|
circleLayer.contentsScale = [UIScreen mainScreen].scale; |
|
[self.layer addSublayer:circleLayer]; |
|
|
|
//关键点数据 |
|
if (self.showLineData) { |
|
UILabel *numLabel = [[UILabel alloc] init]; |
|
numLabel.frame = CGRectMake(point.CGPointValue.x - self.dataTextWidth / 2, point.CGPointValue.y - 18, self.dataTextWidth, self.textFontSize); |
|
numLabel.textColor = self.textColor; |
|
numLabel.textAlignment = NSTextAlignmentRight; |
|
numLabel.font = [UIFont systemFontOfSize:self.textFontSize]; |
|
NSInteger index = i; |
|
if (self.toCenter && self.supplement) { |
|
index = i - 1; |
|
} |
|
numLabel.text = [NSString stringWithFormat:@"%@",self.lineDataAry[index]]; |
|
[self addSubview:numLabel]; |
|
} |
|
} |
|
} |
|
|
|
/* |
|
// Only override drawRect: if you perform custom drawing. |
|
// An empty implementation adversely affects performance during animation. |
|
- (void)drawRect:(CGRect)rect { |
|
// Drawing code |
|
} |
|
*/ |
|
|
|
@end
|
|
|