GBLineChart.m 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. //
  2. // GBLineChart.m
  3. // GBChartDemo
  4. //
  5. // Created by midas on 2018/12/10.
  6. // Copyright © 2018 Midas. All rights reserved.
  7. //
  8. #import "GBLineChart.h"
  9. #import "GBLineChartData.h"
  10. @interface GBLineChart ()
  11. @property(nonatomic) NSMutableArray *chartLineArrayArray; // Array Array[CAShapeLayer] save the line layer
  12. @property(nonatomic) NSMutableArray *chartPointArrayArray; // Array Array[CAShapeLayer] save the point layer
  13. @property(nonatomic) NSMutableArray *linePathArrayArray; // Array of line path, one for each line.
  14. @property(nonatomic) NSMutableArray *pointPathArrayArray;// Array of point path, one for each point.
  15. @property (nonatomic) NSMutableArray *pointValueArrayArray;
  16. @property (nonatomic) NSMutableArray *pointLabelArrayArray;
  17. @property (nonatomic) NSMutableArray *yValueLabelArray;
  18. @property (nonatomic) NSMutableArray *xValueLabelArray;
  19. @property (nonatomic) NSMutableArray *gradientLayerArray;
  20. @property (nonatomic, assign) CGFloat xStep;
  21. @property (nonatomic, assign) CGFloat yStep;
  22. @property (nonatomic) CGFloat yValueMax;
  23. @property (nonatomic) CGFloat yValueMin;
  24. @property (nonatomic) NSInteger yLabelNum;
  25. @property (nonatomic, strong) CAAnimation *strokeEndAnimation;
  26. /**
  27. 坐标轴的高度
  28. */
  29. @property (nonatomic) CGFloat chartCavanHeight;
  30. /**
  31. 坐标轴的宽度
  32. */
  33. @property (nonatomic) CGFloat chartCavanWidth;
  34. @end
  35. @implementation GBLineChart
  36. #pragma mark - 初始化
  37. - (id)initWithCoder:(NSCoder *)aDecoder {
  38. if (self = [super initWithCoder:aDecoder]) {
  39. [self configDefaultValues];
  40. }
  41. return self;
  42. }
  43. - (id)initWithFrame:(CGRect)frame {
  44. if (self = [super initWithFrame:frame]) {
  45. [self configDefaultValues];
  46. }
  47. return self;
  48. }
  49. #pragma mark - 设置默认属性
  50. - (void)configDefaultValues {
  51. self.backgroundColor = [UIColor whiteColor];
  52. self.clipsToBounds = YES;
  53. self.chartLineArrayArray = [NSMutableArray new];
  54. self.chartPointArrayArray = [NSMutableArray new];
  55. self.pointPathArrayArray = [NSMutableArray new];
  56. self.linePathArrayArray = [NSMutableArray new];
  57. self.pointLabelArrayArray = [NSMutableArray new];
  58. self.pointValueArrayArray = [NSMutableArray new];
  59. self.yValueLabelArray = [NSMutableArray new];
  60. self.xValueLabelArray = [NSMutableArray new];
  61. self.gradientLayerArray = [NSMutableArray new];
  62. _displayAnimated = YES;
  63. _showCoordinateAxis = YES;
  64. _showYGridsLineDash = YES;
  65. _showYLabels = YES;
  66. _coordinateAxisLineWidth = 1;
  67. _coordinateAxisColor = [UIColor darkGrayColor];
  68. _xAxisColor = _coordinateAxisColor;
  69. _yAxisColor = _coordinateAxisColor;
  70. _showYGridsLine = YES;
  71. _yGridsLineColor = [UIColor lightGrayColor];
  72. _yGridsLineWidth = 1;
  73. _chartMarginLeft = 25.0;
  74. _chartMarginRight = 25.0;
  75. _chartMarginTop = 0.0;
  76. _chartMarginBottom = 25.0;
  77. _xLabelColor = [UIColor grayColor];
  78. _xLabelFont = [UIFont systemFontOfSize:10];
  79. _yLabelFont = [UIFont systemFontOfSize:10];
  80. _yLabelColor = [UIColor grayColor];
  81. _yLabelFormat = @"%.0f";
  82. _verticalLineXValue = 0;
  83. _showVerticalLine = NO;
  84. _verticalLineWidth = 1;
  85. _verticalLineColor = [UIColor blackColor];
  86. }
  87. #pragma mark - setter方法
  88. - (void)setCoordinateAxisColor:(UIColor *)coordinateAxisColor {
  89. _coordinateAxisColor = coordinateAxisColor;
  90. _xAxisColor = coordinateAxisColor;
  91. _yAxisColor = coordinateAxisColor;
  92. }
  93. #pragma mark - Public Method
  94. #pragma mark - 画图表
  95. - (void)strokeChart {
  96. [self calcuateChart];
  97. [self setXLabel];
  98. if (_showYLabels) {
  99. [self setYLabel];
  100. }
  101. [self setGradientLayer];
  102. [self populateChartLines];
  103. if (_displayAnimated) {
  104. [self addAnimationIfNeeded];
  105. }
  106. [self createPointLabel];
  107. if (_showVerticalLine) {
  108. [self strokeVerticalLine];
  109. }
  110. [self setNeedsDisplay];
  111. }
  112. #pragma mark - 画竖线
  113. - (void)strokeVerticalLine {
  114. UIBezierPath *path = [UIBezierPath bezierPath];
  115. CGFloat x = [self getXPointWithXValue:_verticalLineXValue item:0];
  116. [path moveToPoint:CGPointMake(x, _chartMarginTop+_chartCavanHeight)];
  117. [path addLineToPoint:CGPointMake(x, _chartMarginTop)];
  118. CAShapeLayer *layer = [CAShapeLayer layer];
  119. layer.strokeColor = _verticalLineColor.CGColor;
  120. layer.lineWidth = _verticalLineWidth;
  121. layer.path = path.CGPath;
  122. [self.layer addSublayer:layer];
  123. }
  124. #pragma mark - 获取x方向的坐标(位置)
  125. - (CGFloat)getXPointWithXValue:(CGFloat)xValue item:(NSInteger)item{
  126. NSLog(@"%ld",item);
  127. CGFloat x;
  128. if (xValue == 0) {
  129. if (_xLabelAlignmentStyle == GBXLabelAlignmentStyleFullXAxis) {
  130. x = _chartMarginLeft + item *_xStep;
  131. } else {
  132. x = _chartMarginLeft + _xStep/2 + item*_xStep;
  133. }
  134. return x;
  135. }
  136. if (_xLabelTitles.count == 1) {
  137. return _chartMarginLeft + _xStep/2;
  138. }
  139. // CGFloat xMaxValue = [_xLabelTitles.lastObject floatValue];
  140. //
  141. // CGFloat xMinValue = [_xLabelTitles.firstObject floatValue];
  142. xValue = item;
  143. CGFloat xMaxValue = 6;
  144. CGFloat xMinValue = 0;
  145. NSLog(@"xValue = %lf,xMaxValue = %lf,xMinValue = %lf",xValue,xMaxValue,xMinValue);
  146. //
  147. // if (_xLabelAlignmentStyle == GBXLabelAlignmentStyleFullXAxis) {
  148. // CGFloat position = (xValue-xMinValue) / (xMaxValue-xMinValue)* _chartCavanWidth;
  149. // x = _chartMarginLeft + position;
  150. // } else {
  151. // CGFloat position = (xValue-xMinValue) / (xMaxValue-xMinValue)* _xStep*(_xLabelTitles.count-1);
  152. // x = _chartMarginLeft + _xStep/2 + position;
  153. // }
  154. if (_xLabelAlignmentStyle == GBXLabelAlignmentStyleFullXAxis) {
  155. CGFloat position = (xValue-xMinValue) / 6* _chartCavanWidth;
  156. x = _chartMarginLeft + position;
  157. } else {
  158. CGFloat position = (xValue-xMinValue) / 6* _xStep*(_xLabelTitles.count-1);
  159. x = _chartMarginLeft + _xStep/2 + position;
  160. }
  161. return x;
  162. }
  163. #pragma mark - 更新图表
  164. - (void)updateChartDatas:(NSArray <GBLineChartData *> *)data {
  165. [self removeAllLayers];
  166. [self removeAllSubviews];
  167. [self removeAllObjects];
  168. _lineChartDatas = data;
  169. [self strokeChart];
  170. }
  171. #pragma mark - 创建点Label
  172. - (void)createPointLabel {
  173. for (int i = 0; i < _lineChartDatas.count; i++) {
  174. GBLineChartData *chartData = _lineChartDatas[i];
  175. NSArray <NSValue *> *pointValueArray = _pointValueArrayArray[i];
  176. if (chartData.showPointLabel) {//显示点
  177. NSInteger item = 0;
  178. NSMutableArray *pointLabelArray = [NSMutableArray array];
  179. for (NSValue *value in pointValueArray) {
  180. UILabel *label = [UILabel new];
  181. label.font = chartData.pointLabelFont;
  182. label.textColor = chartData.pointLabelColor;
  183. label.textAlignment = NSTextAlignmentCenter;
  184. label.text = [NSString stringWithFormat:chartData.pointLabelFormat, chartData.dataGetter(item).y];
  185. CGPoint position = value.CGPointValue;
  186. CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName : label.font}];
  187. label.frame = CGRectMake(position.x - size.width/2, position.y - size.height-chartData.inflexionPointWidth, size.width, size.height);
  188. [self addSubview:label];
  189. [pointLabelArray addObject:label];
  190. item++;
  191. }
  192. [self.pointLabelArrayArray addObject:pointLabelArray];
  193. }
  194. }
  195. }
  196. #pragma mark - 添加动画
  197. - (void)addAnimationIfNeeded {
  198. for (int i = 0; i < _chartLineArrayArray.count; i++) {
  199. NSArray <CAShapeLayer *> *chartLineArr = _chartLineArrayArray[i];
  200. for (CAShapeLayer *line in chartLineArr) {
  201. [line addAnimation:self.strokeEndAnimation forKey:@"ss"];
  202. }
  203. }
  204. for (int i = 0; i < _chartPointArrayArray.count; i++) {
  205. NSArray <CAShapeLayer *> *chartPointArr = _chartPointArrayArray[i];
  206. for (CAShapeLayer *point in chartPointArr) {
  207. [point addAnimation:self.strokeEndAnimation forKey:@"11"];
  208. }
  209. }
  210. }
  211. #pragma mark - strokeEndAnimation
  212. - (CAAnimation *)strokeEndAnimation {
  213. if (!_strokeEndAnimation) {
  214. CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
  215. ani.fromValue = @0;
  216. ani.toValue = @1;
  217. ani.duration = 1;
  218. _strokeEndAnimation= ani;
  219. }
  220. return _strokeEndAnimation;
  221. }
  222. #pragma mark - 获取Y最大值最小值
  223. - (void)getYValueMaxAndYValueMin {
  224. if (_yLabelTitles) {
  225. _yValueMax = [_yLabelTitles.lastObject floatValue];
  226. _yValueMin = [_yLabelTitles.firstObject floatValue];
  227. _yLabelNum = _yLabelTitles.count;
  228. } else {
  229. for (int i = 0; i < _lineChartDatas.count; i++) {
  230. GBLineChartData *chartData = _lineChartDatas[i];
  231. for (int j = 0; j < chartData.itemCount; j++) {
  232. CGFloat yValue = chartData.dataGetter(j).y;
  233. _yValueMax = MAX(_yValueMax, yValue);
  234. _yValueMin = MIN(_yValueMin, yValue);
  235. }
  236. }
  237. _yLabelNum = 6;
  238. }
  239. }
  240. #pragma mark - 计算
  241. - (void)calcuateChart {
  242. _chartCavanWidth = self.frame.size.width - _chartMarginLeft - _chartMarginRight;
  243. _chartCavanHeight = self.frame.size.height - _chartMarginBottom - _chartMarginTop;
  244. [self getYValueMaxAndYValueMin];
  245. if (_xLabelAlignmentStyle == GBXLabelAlignmentStyleFullXAxis) {
  246. _xStep = _chartCavanWidth/(_xLabelTitles.count-1);
  247. } else {
  248. _xStep = _chartCavanWidth/_xLabelTitles.count;
  249. }
  250. _yStep = _chartCavanHeight/(_yLabelNum-1);
  251. CGFloat yAxisMax = _chartCavanHeight + _chartMarginTop;
  252. for (int i = 0; i < _lineChartDatas.count; i++) {
  253. GBLineChartData *chartData = _lineChartDatas[i];
  254. //点的数组
  255. NSMutableArray *pointValueArray = [NSMutableArray array];
  256. NSArray *array = @[@"0",@"1",@"2",@"3",@"4",@"5",@"6"];
  257. for (NSInteger item = chartData.startIndex; item < chartData.itemCount+chartData.startIndex; item++) {
  258. ///点的横坐标
  259. CGFloat center_x = [self getXPointWithXValue:chartData.dataGetter(item).x item:item];
  260. CGFloat yValue1 = chartData.dataGetter(item).y;
  261. if (yValue1 < _yValueMin || yValue1 > _yValueMax) {
  262. @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"值只能在最小值与最大值之间" userInfo:nil];
  263. }
  264. ///点的纵坐标
  265. CGFloat center_y = yAxisMax - (yValue1-_yValueMin)/(_yValueMax-_yValueMin) * (_chartCavanHeight-_yStep);
  266. NSValue *pointValue = [NSValue valueWithCGPoint:CGPointMake(center_x, center_y)];
  267. [pointValueArray addObject:pointValue];
  268. }
  269. [_pointValueArrayArray addObject:pointValueArray];
  270. //点
  271. NSMutableArray *pointPathArray = [NSMutableArray array];
  272. for (NSInteger item = chartData.startIndex; item < chartData.itemCount+chartData.startIndex; item++) {
  273. CGFloat center_x = [self getXPointWithXValue:chartData.dataGetter(item).x item:item];
  274. CGFloat center_y = yAxisMax - (chartData.dataGetter(item).y-_yValueMin)/(_yValueMax-_yValueMin) * (_chartCavanHeight-_yStep);
  275. UIBezierPath *path = [UIBezierPath bezierPath];
  276. if (chartData.lineChartPointStyle == GBLineChartPointStyleCircle) {//圆
  277. path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(center_x, center_y) radius:chartData.inflexionPointWidth/2 startAngle:0 endAngle:2*M_PI clockwise:YES];
  278. } else if (chartData.lineChartPointStyle == GBLineChartPointStyleSquare) {//方形
  279. path = [UIBezierPath bezierPathWithRect:CGRectMake(center_x-chartData.inflexionPointWidth/2, center_y - chartData.inflexionPointWidth/2, chartData.inflexionPointWidth, chartData.inflexionPointWidth)];
  280. } else if (chartData.lineChartPointStyle == GBLineChartPointStyleTriangle) { //三角形
  281. path = [UIBezierPath bezierPath];
  282. CGFloat x1 = center_x;
  283. CGFloat y1 = center_y - chartData.inflexionPointWidth;
  284. CGFloat x2 = center_x - chartData.inflexionPointWidth * sinf(M_PI/3);
  285. CGFloat y2 = center_y + chartData.inflexionPointWidth * cosf(M_PI/3);
  286. CGFloat x3 = center_x + chartData.inflexionPointWidth * sinf(M_PI/3);
  287. CGFloat y3 = y2;
  288. [path moveToPoint:CGPointMake(x1, y1)];
  289. [path addLineToPoint:CGPointMake(x2, y2)];
  290. [path addLineToPoint:CGPointMake(x3, y3)];
  291. [path closePath];
  292. }
  293. [pointPathArray addObject:path];
  294. }
  295. [_pointPathArrayArray addObject:pointPathArray];
  296. }
  297. if (_showSmoothLines) { //曲线
  298. for (int i = 0; i < _pointValueArrayArray.count; i++) {
  299. NSMutableArray *pointValueArray = _pointValueArrayArray[i];
  300. if (pointValueArray.count==0) {
  301. continue;
  302. }
  303. CGPoint startPoint;
  304. CGPoint endPoint;
  305. // if (i == 0) {
  306. // startPoint = [pointValueArray[0] CGPointValue];
  307. // } else{
  308. //// startPoint = [pointValueArray];
  309. // }
  310. startPoint = CGPointMake(startPoint.x-_xStep, startPoint.y);
  311. endPoint = [pointValueArray.lastObject CGPointValue];
  312. endPoint = CGPointMake(endPoint.x+_xStep, endPoint.y);
  313. [pointValueArray insertObject:[NSValue valueWithCGPoint:startPoint] atIndex:0];
  314. [pointValueArray addObject:[NSValue valueWithCGPoint:endPoint]];
  315. NSMutableArray *chartLinePathArray = [NSMutableArray array];
  316. for (int j = 0; j < pointValueArray.count-3; j++) {
  317. CGPoint p0 = [pointValueArray[j] CGPointValue];
  318. CGPoint p1 = [pointValueArray[j+1] CGPointValue];
  319. CGPoint p2 = [pointValueArray[j+2] CGPointValue];
  320. CGPoint p3 = [pointValueArray[j+3] CGPointValue];
  321. UIBezierPath *path = [UIBezierPath bezierPath];
  322. [path moveToPoint:p1];
  323. if (p1.y == _chartCavanHeight + _chartMarginTop && p2.y == _chartCavanHeight + _chartMarginTop) {
  324. [self getLinePathx1:p2.x andy1:p2.y path:path];
  325. } else {
  326. [self getControlPointx0:p0.x andy0:p0.y x1:p1.x andy1:p1.y x2:p2.x andy2:p2.y x3:p3.x andy3:p3.y path:path];
  327. }
  328. [chartLinePathArray addObject:path];
  329. }
  330. [pointValueArray removeObjectAtIndex:0];
  331. [pointValueArray removeLastObject];
  332. [_linePathArrayArray addObject:chartLinePathArray];
  333. }
  334. }
  335. #if 1
  336. else {//直线
  337. for (int i = 0; i < _pointValueArrayArray.count; i++) {
  338. NSArray *pointValueArray = _pointValueArrayArray[i];
  339. NSMutableArray *chartLinePathArray = [NSMutableArray array];
  340. for (int j = 0; j < pointValueArray.count-1; j++) {
  341. CGPoint point0 = [pointValueArray[j] CGPointValue];
  342. CGPoint point1 = [pointValueArray[j+1] CGPointValue];
  343. UIBezierPath *path = [UIBezierPath bezierPath];
  344. [path moveToPoint:point0];
  345. [path addLineToPoint:point1];
  346. [chartLinePathArray addObject:path];
  347. }
  348. [_linePathArrayArray addObject:chartLinePathArray];
  349. }
  350. }
  351. #endif
  352. }
  353. #pragma mark - 布局折线和点
  354. - (void)populateChartLines {
  355. //线
  356. for (int i = 0; i < _linePathArrayArray.count; i++) {
  357. GBLineChartData *chartData = _lineChartDatas[i];
  358. NSMutableArray <UIBezierPath *> *linePathArray = _linePathArrayArray[i];
  359. NSMutableArray *chartLineArray = [NSMutableArray array];
  360. for (UIBezierPath *path in linePathArray) {
  361. CAShapeLayer *line = [CAShapeLayer layer];
  362. line.lineCap = kCALineCapButt;
  363. line.lineJoin = kCALineJoinMiter;
  364. line.lineWidth = chartData.lineWidth;
  365. line.strokeColor = [chartData.lineColor colorWithAlphaComponent:chartData.lineAlpha].CGColor;
  366. line.path = path.CGPath;
  367. line.fillColor = [UIColor clearColor].CGColor;
  368. if (chartData.showDash) {
  369. line.lineDashPattern = chartData.lineDashPattern;
  370. }
  371. [self.layer addSublayer:line];
  372. [chartLineArray addObject:line];
  373. }
  374. [_chartLineArrayArray addObject:chartLineArray];
  375. }
  376. //点
  377. for (int i = 0; i < _pointPathArrayArray.count; i++) {
  378. GBLineChartData *chartData = _lineChartDatas[i];
  379. NSMutableArray <UIBezierPath *> *pointPathArray = _pointPathArrayArray[i];
  380. NSMutableArray *pointLayerArr = [NSMutableArray array];
  381. for (UIBezierPath *path in pointPathArray) {
  382. CAShapeLayer *pointLayer = [CAShapeLayer layer];
  383. pointLayer.strokeColor = chartData.inflexionPointStrokeColor.CGColor;
  384. pointLayer.fillColor = chartData.inflexionPointFillColor.CGColor;
  385. pointLayer.path = path.CGPath;
  386. [self.layer addSublayer:pointLayer];
  387. [pointLayerArr addObject:pointLayer];
  388. }
  389. [_chartPointArrayArray addObject:pointLayerArr];
  390. }
  391. }
  392. #pragma mark - 移除
  393. #pragma mark 移除所有layers
  394. - (void)removeAllLayers {
  395. for (NSArray <CALayer *> *layers in self.chartPointArrayArray) {
  396. for (CALayer *layer in layers) {
  397. [layer removeAllAnimations];
  398. [layer removeFromSuperlayer];
  399. }
  400. }
  401. for (NSArray <CALayer *> *layers in self.chartLineArrayArray) {
  402. for (CALayer *layer in layers) {
  403. [layer removeAllAnimations];
  404. [layer removeFromSuperlayer];
  405. }
  406. }
  407. for (CALayer *layer in self.gradientLayerArray) {
  408. [layer removeAllAnimations];
  409. [layer removeFromSuperlayer];
  410. }
  411. }
  412. #pragma mark 移除所有subviews
  413. - (void)removeAllSubviews {
  414. for (NSArray <UILabel *> *views in _pointLabelArrayArray) {
  415. for (UILabel *label in views) {
  416. [label removeFromSuperview];
  417. }
  418. }
  419. for (UILabel *label in _yValueLabelArray) {
  420. [label removeFromSuperview];
  421. }
  422. for (UILabel *label in _xValueLabelArray) {
  423. [label removeFromSuperview];
  424. }
  425. }
  426. #pragma mark 移除所有数据
  427. - (void)removeAllObjects {
  428. [self.pointValueArrayArray removeAllObjects];
  429. [self.chartPointArrayArray removeAllObjects];
  430. [self.pointLabelArrayArray removeAllObjects];
  431. [self.chartLineArrayArray removeAllObjects];
  432. [self.linePathArrayArray removeAllObjects];
  433. [self.pointPathArrayArray removeAllObjects];
  434. [self.yValueLabelArray removeAllObjects];
  435. [self.xValueLabelArray removeAllObjects];
  436. }
  437. #pragma mark - 创建坐标轴的label
  438. - (void)setYLabel {
  439. //从下往上布局
  440. CGFloat divideSeperetor = (_yValueMax - _yValueMin)/(_yLabelNum-1);
  441. for (int i = 0; i < _yLabelNum; i++) {
  442. UILabel *label = [[UILabel alloc] initWithFrame:[self frameForYLabelAtIndex:i]];
  443. label.font = _yLabelFont;
  444. label.textColor = _yLabelColor;
  445. NSString *value = [NSString stringWithFormat:_yLabelFormat, _yValueMin + divideSeperetor*i];
  446. if (_yLabelBlockFormatter) {
  447. value = _yLabelBlockFormatter(_yValueMin + divideSeperetor*i);
  448. }
  449. label.text = value;
  450. label.textAlignment = NSTextAlignmentRight;
  451. [self addSubview:label];
  452. [self.yValueLabelArray addObject:label];
  453. }
  454. }
  455. - (void)setXLabel{
  456. //从左到右
  457. for (int i = 0; i < _xLabelTitles.count; i++) {
  458. UILabel *label = [[UILabel alloc] initWithFrame:[self frameForXLabelAtIndex:i]];
  459. label.font = _xLabelFont;
  460. label.textColor = _xLabelColor;
  461. label.text = _xLabelTitles[i];
  462. label.textAlignment = NSTextAlignmentCenter;
  463. label.transform = CGAffineTransformMakeRotation(_xLabelRotationAngle);
  464. [self addSubview:label];
  465. [self.xValueLabelArray addObject:label];
  466. }
  467. }
  468. #pragma mark - 创建渐变图层
  469. - (void)setGradientLayer {
  470. for (int i = 0; i < _pointValueArrayArray.count; i++) {
  471. NSMutableArray *pointValueArray = _pointValueArrayArray[i];
  472. if (!_lineChartDatas[i].showGradientArea) {
  473. continue;
  474. }
  475. UIBezierPath *path = [UIBezierPath bezierPath];
  476. if (_showSmoothLines) { //曲线
  477. if (pointValueArray.count==0) {
  478. continue;
  479. }
  480. CGPoint startPoint = [pointValueArray[0] CGPointValue];
  481. startPoint = CGPointMake(startPoint.x-_xStep, startPoint.y);
  482. CGPoint endPoint = [pointValueArray.lastObject CGPointValue];
  483. endPoint = CGPointMake(endPoint.x+_xStep, endPoint.y);
  484. [pointValueArray insertObject:[NSValue valueWithCGPoint:startPoint] atIndex:0];
  485. [pointValueArray addObject:[NSValue valueWithCGPoint:endPoint]];
  486. for (int j = 0; j < pointValueArray.count - 3;j++) {
  487. CGPoint p0 = [pointValueArray[j] CGPointValue];
  488. CGPoint p1 = [pointValueArray[j+1] CGPointValue];
  489. CGPoint p2 = [pointValueArray[j+2] CGPointValue];
  490. CGPoint p3 = [pointValueArray[j+3] CGPointValue];
  491. if (j == 0) {
  492. [path moveToPoint:p1];
  493. }
  494. if (p1.y == 0.0 && p2.y == 0.0) {
  495. [self getLinePathx1:p2.x andy1:p2.y path:path];
  496. } else {
  497. [self getControlPointx0:p0.x andy0:p0.y x1:p1.x andy1:p1.y x2:p2.x andy2:p2.y x3:p3.x andy3:p3.y path:path];
  498. }
  499. }
  500. [pointValueArray removeObjectAtIndex:0];
  501. [pointValueArray removeLastObject];
  502. } else { //直线
  503. for (int j = 0 ; j < pointValueArray.count; j++) {
  504. if (j == 0) {
  505. [path moveToPoint:[pointValueArray[j] CGPointValue]];
  506. }
  507. [path addLineToPoint:[pointValueArray[j] CGPointValue]];
  508. }
  509. }
  510. CGPoint point1 = [pointValueArray.firstObject CGPointValue];
  511. CGFloat yAxisMax = _chartCavanHeight + _chartMarginTop;
  512. point1 = CGPointMake(point1.x, yAxisMax);
  513. CGPoint point2 = [pointValueArray.lastObject CGPointValue];
  514. point2 = CGPointMake(point2.x, yAxisMax);
  515. [path addLineToPoint:point2];
  516. [path addLineToPoint:point1];
  517. [path closePath];
  518. CAShapeLayer *maskLayer = [CAShapeLayer layer];
  519. maskLayer.path = path.CGPath;
  520. maskLayer.strokeColor = [UIColor clearColor].CGColor;
  521. maskLayer.fillColor = [UIColor blackColor].CGColor;
  522. CAGradientLayer *gradientLayer = [CAGradientLayer layer];
  523. gradientLayer.frame = self.bounds;
  524. [self.layer addSublayer:gradientLayer];
  525. gradientLayer.colors = @[(__bridge id)_lineChartDatas[i].startGradientColor.CGColor, (__bridge id)_lineChartDatas[i].endGradientColor.CGColor];
  526. gradientLayer.startPoint = CGPointMake(0.5, 0);
  527. gradientLayer.endPoint = CGPointMake(0.5, 1);
  528. gradientLayer.mask = maskLayer;
  529. gradientLayer.zPosition = -10;
  530. [self.gradientLayerArray addObject:gradientLayer];
  531. CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"opacity"];
  532. ani.fromValue = @0;
  533. ani.toValue = @1;
  534. ani.duration = 1.5;
  535. [gradientLayer addAnimation:ani forKey:nil];
  536. }
  537. }
  538. #pragma mark - YLabel Frame XLabel Frame
  539. - (CGRect)frameForYLabelAtIndex:(NSInteger)index {
  540. CGFloat yAxisMax = _chartCavanHeight + _chartMarginTop;
  541. CGFloat w = _chartMarginLeft-4;
  542. _yLabelHeight = _chartCavanHeight / _yLabelNum;
  543. CGFloat x = 0;
  544. CGFloat y = yAxisMax - index* _yLabelHeight - _yLabelHeight/2;
  545. return CGRectMake(x, y, w, _yLabelHeight);
  546. }
  547. - (CGRect)frameForXLabelAtIndex:(NSInteger)index {
  548. CGFloat yAxisMax = _chartCavanHeight + _chartMarginTop;
  549. _xLabelWidth = [self getMaxXLabelWidth];
  550. CGFloat h = _chartMarginBottom;
  551. CGFloat x = 0;
  552. CGFloat y = yAxisMax;
  553. if (_xLabelAlignmentStyle == GBXLabelAlignmentStyleFullXAxis) {
  554. x = _chartMarginLeft + index * _xStep - _xStep/2;
  555. } else{
  556. x = _chartMarginLeft + index * _xStep;
  557. }
  558. return CGRectMake(x, y, _xLabelWidth, h);
  559. }
  560. - (CGFloat)getMaxXLabelWidth {
  561. CGFloat width = _chartCavanWidth/_xLabelTitles.count;
  562. for (NSString *text in _xLabelTitles) {
  563. CGFloat tempW = [text sizeWithAttributes:@{NSFontAttributeName : _xLabelFont}].width;
  564. width = MAX(width, tempW);
  565. }
  566. return ceil(width);
  567. }
  568. - (void)getLinePathx1:(CGFloat)x1 andy1:(CGFloat)y1
  569. path:(UIBezierPath*)path {
  570. [path addLineToPoint:CGPointMake(x1, y1)];
  571. }
  572. - (void)getControlPointx0:(CGFloat)x0 andy0:(CGFloat)y0
  573. x1:(CGFloat)x1 andy1:(CGFloat)y1
  574. x2:(CGFloat)x2 andy2:(CGFloat)y2
  575. x3:(CGFloat)x3 andy3:(CGFloat)y3
  576. path:(UIBezierPath*)path{
  577. CGFloat smooth_value = 0.6;
  578. CGFloat ctrl1_x;
  579. CGFloat ctrl1_y;
  580. CGFloat ctrl2_x;
  581. CGFloat ctrl2_y;
  582. CGFloat xc1 = (x0 + x1) / 2.0;
  583. CGFloat yc1 = (y0 + y1) / 2.0;
  584. CGFloat xc2 = (x1 + x2) / 2.0;
  585. CGFloat yc2 = (y1 + y2) / 2.0;
  586. CGFloat xc3 = (x2 + x3) / 2.0;
  587. CGFloat yc3 = (y2 + y3) / 2.0;
  588. CGFloat len1 = sqrt(pow((x1-x0), 2) + pow(y1-y0, 2));
  589. CGFloat len2 = sqrt(pow((x2-x1), 2) + pow(y2-y1, 2));
  590. CGFloat len3 = sqrt(pow((x3-x2), 2) + pow((y3-y2), 2));
  591. CGFloat k1 = len1 / (len1 + len2);
  592. CGFloat k2 = len2 / (len2 + len3);
  593. CGFloat xm1 = xc1 + (xc2 - xc1) * k1;
  594. CGFloat ym1 = yc1 + (yc2 - yc1) * k1;
  595. CGFloat xm2 = xc2 + (xc3 - xc2) * k2;
  596. CGFloat ym2 = yc2 + (yc3 - yc2) * k2;
  597. ctrl1_x = xm1 + (xc2 - xm1) * smooth_value + x1 - xm1;
  598. ctrl1_y = ym1 + (yc2 - ym1) * smooth_value + y1 - ym1;
  599. ctrl2_x = xm2 + (xc2 - xm2) * smooth_value + x2 - xm2;
  600. ctrl2_y = ym2 + (yc2 - ym2) * smooth_value + y2 - ym2;
  601. [path addCurveToPoint:CGPointMake(x2, y2) controlPoint1:CGPointMake(ctrl1_x, ctrl1_y) controlPoint2:CGPointMake(ctrl2_x, ctrl2_y)];
  602. }
  603. #pragma mark - 绘制
  604. - (void)drawRect:(CGRect)rect {
  605. [super drawRect:rect];
  606. CGFloat yAxisMax = _chartCavanHeight + _chartMarginTop;
  607. CGFloat xAxisMax = _chartCavanWidth + _chartMarginLeft;
  608. if (_showCoordinateAxis) {
  609. //画坐标轴
  610. CGContextRef ctx = UIGraphicsGetCurrentContext();
  611. CGContextSetLineWidth(ctx, _coordinateAxisLineWidth);
  612. CGContextSetStrokeColorWithColor(ctx, _yAxisColor.CGColor);
  613. //画y轴三角形
  614. CGContextMoveToPoint(ctx, _chartMarginLeft, _chartMarginTop);
  615. CGContextAddLineToPoint(ctx, _chartMarginLeft-3, _chartMarginTop+5);
  616. CGContextMoveToPoint(ctx, _chartMarginLeft, _chartMarginTop);
  617. CGContextAddLineToPoint(ctx, _chartMarginLeft+3, _chartMarginTop+5);
  618. //画y轴线
  619. CGContextMoveToPoint(ctx, _chartMarginLeft, _chartMarginTop);
  620. CGContextAddLineToPoint(ctx, _chartMarginLeft, yAxisMax);
  621. //绘制y轴分割点
  622. for (int i = 0; i < _yLabelNum; i++) {
  623. CGFloat y = _chartMarginTop + _yStep * i + _yStep;
  624. CGContextMoveToPoint(ctx, _chartMarginLeft+3, y);
  625. CGContextAddLineToPoint(ctx, _chartMarginLeft-2, y);
  626. }
  627. CGContextStrokePath(ctx);
  628. CGContextSetStrokeColorWithColor(ctx, _xAxisColor.CGColor);
  629. //画x轴三角形
  630. CGContextMoveToPoint(ctx, _chartMarginLeft, yAxisMax);
  631. CGContextAddLineToPoint(ctx, xAxisMax, yAxisMax);
  632. CGContextAddLineToPoint(ctx, xAxisMax-5, yAxisMax-3);
  633. //画x轴线
  634. CGContextMoveToPoint(ctx, xAxisMax, yAxisMax);
  635. CGContextAddLineToPoint(ctx, xAxisMax-5, yAxisMax+3);
  636. //绘制x轴分割点
  637. CGFloat y = _chartCavanHeight + _chartMarginTop;
  638. for (NSInteger i = 0; i < _xLabelTitles.count; i++) {
  639. CGFloat x = _chartMarginLeft + (_xLabelAlignmentStyle == GBXLabelAlignmentStyleFullXAxis ? 0 : _xStep/2) + i * _xStep;
  640. CGContextMoveToPoint(ctx, x, y-3);
  641. CGContextAddLineToPoint(ctx, x, y);
  642. }
  643. CGContextStrokePath(ctx);
  644. }
  645. if (_showYGridsLine) {
  646. //绘制横线
  647. CGContextRef ctx = UIGraphicsGetCurrentContext();
  648. CGContextSetStrokeColorWithColor(ctx, _yGridsLineColor.CGColor);
  649. CGContextSetLineWidth(ctx, _yGridsLineWidth);
  650. if (_showYGridsLineDash) {
  651. CGFloat dash[] = {3,3};
  652. CGContextSetLineDash(ctx, 0.0, dash, 2 );
  653. }
  654. NSInteger index= 0;
  655. if (_showCoordinateAxis) {
  656. index = 1;
  657. }
  658. for (NSInteger i = index; i < _yLabelNum; i++) {
  659. CGContextMoveToPoint(ctx, _chartMarginLeft, yAxisMax - _yStep * i);
  660. CGContextAddLineToPoint(ctx, xAxisMax, yAxisMax - _yStep * i);
  661. CGContextStrokePath(ctx);
  662. }
  663. }
  664. }
  665. @end