Kevin

Merge pull request #62 from jackyzonewen/master

PNLineChart's inflexion Point support two sharp, such as cycle, square
@@ -10,7 +10,6 @@ @@ -10,7 +10,6 @@
10 #import <QuartzCore/QuartzCore.h> 10 #import <QuartzCore/QuartzCore.h>
11 #import "PNChartDelegate.h" 11 #import "PNChartDelegate.h"
12 12
13 -  
14 @interface PNLineChart : UIView 13 @interface PNLineChart : UIView
15 14
16 /** 15 /**
@@ -48,8 +47,6 @@ @@ -48,8 +47,6 @@
48 47
49 @property (nonatomic) CGFloat chartMargin; 48 @property (nonatomic) CGFloat chartMargin;
50 49
51 -  
52 -  
53 @property (nonatomic) BOOL showLabel; 50 @property (nonatomic) BOOL showLabel;
54 51
55 52
@@ -18,9 +18,11 @@ @@ -18,9 +18,11 @@
18 //------------------------------------------------------------------------------------------------ 18 //------------------------------------------------------------------------------------------------
19 @interface PNLineChart () 19 @interface PNLineChart ()
20 20
21 -@property (nonatomic) NSMutableArray *chartLineArray; // Array[CAShapeLayer] 21 +@property (nonatomic) NSMutableArray *chartLineArray; // Array[CAShapeLayer]
  22 +@property (nonatomic) NSMutableArray *chartPointArray; // Array[CAShapeLayer] save the point layer
22 23
23 -@property (nonatomic) NSMutableArray *chartPath; //Array of line path, one for each line. 24 +@property (nonatomic) NSMutableArray *chartPath; // Array of line path, one for each line.
  25 +@property (nonatomic) NSMutableArray *pointPath; // Array of point path, one for each line
24 26
25 - (void)setDefaultValues; 27 - (void)setDefaultValues;
26 28
@@ -182,18 +184,29 @@ @@ -182,18 +184,29 @@
182 - (void)strokeChart 184 - (void)strokeChart
183 { 185 {
184 _chartPath = [[NSMutableArray alloc] init]; 186 _chartPath = [[NSMutableArray alloc] init];
  187 + _pointPath = [[NSMutableArray alloc] init];
185 188
186 //Draw each line 189 //Draw each line
187 for (NSUInteger lineIndex = 0; lineIndex < self.chartData.count; lineIndex++) { 190 for (NSUInteger lineIndex = 0; lineIndex < self.chartData.count; lineIndex++) {
188 PNLineChartData *chartData = self.chartData[lineIndex]; 191 PNLineChartData *chartData = self.chartData[lineIndex];
189 CAShapeLayer *chartLine = (CAShapeLayer *)self.chartLineArray[lineIndex]; 192 CAShapeLayer *chartLine = (CAShapeLayer *)self.chartLineArray[lineIndex];
  193 + CAShapeLayer *pointLayer = (CAShapeLayer *)self.chartPointArray[lineIndex];
  194 +
190 CGFloat yValue; 195 CGFloat yValue;
191 CGFloat innerGrade; 196 CGFloat innerGrade;
192 - CGPoint point;  
193 197
194 UIGraphicsBeginImageContext(self.frame.size); 198 UIGraphicsBeginImageContext(self.frame.size);
  199 +
195 UIBezierPath *progressline = [UIBezierPath bezierPath]; 200 UIBezierPath *progressline = [UIBezierPath bezierPath];
  201 + [progressline setLineWidth:chartData.lineWidth];
  202 + [progressline setLineCapStyle:kCGLineCapRound];
  203 + [progressline setLineJoinStyle:kCGLineJoinRound];
  204 +
  205 + UIBezierPath *pointPath = [UIBezierPath bezierPath];
  206 + [pointPath setLineWidth:chartData.lineWidth];
  207 +
196 [_chartPath addObject:progressline]; 208 [_chartPath addObject:progressline];
  209 + [_pointPath addObject:pointPath];
197 210
198 if (!_showLabel) { 211 if (!_showLabel) {
199 _chartCavanHeight = self.frame.size.height - 2 * _yLabelHeight; 212 _chartCavanHeight = self.frame.size.height - 2 * _yLabelHeight;
@@ -203,23 +216,91 @@ @@ -203,23 +216,91 @@
203 } 216 }
204 217
205 NSMutableArray *linePointsArray = [[NSMutableArray alloc] init]; 218 NSMutableArray *linePointsArray = [[NSMutableArray alloc] init];
206 - [progressline setLineWidth:3.0];  
207 - [progressline setLineCapStyle:kCGLineCapRound];  
208 - [progressline setLineJoinStyle:kCGLineJoinRound];  
209 219
  220 + int last_x = 0;
  221 + int last_y = 0;
  222 + CGFloat inflexionWidth = chartData.inflexionPointWidth;
  223 +
210 for (NSUInteger i = 0; i < chartData.itemCount; i++) { 224 for (NSUInteger i = 0; i < chartData.itemCount; i++) {
  225 +
211 yValue = chartData.getData(i).y; 226 yValue = chartData.getData(i).y;
212 227
213 innerGrade = (yValue - _yValueMin) / (_yValueMax - _yValueMin); 228 innerGrade = (yValue - _yValueMin) / (_yValueMax - _yValueMin);
214 - 229 +
215 - point = CGPointMake(2 * _chartMargin + (i * _xLabelWidth), _chartCavanHeight - (innerGrade * _chartCavanHeight) + (_yLabelHeight / 2)); 230 + int x = 2 * _chartMargin + (i * _xLabelWidth);
216 - 231 + int y = _chartCavanHeight - (innerGrade * _chartCavanHeight) + (_yLabelHeight / 2);
217 - if (i != 0) { 232 +
218 - [progressline addLineToPoint:point]; 233 + // cycle style point
  234 + if (chartData.inflexionPointStyle == PNLineChartPointStyleCycle) {
  235 +
  236 + CGRect circleRect = CGRectMake(x-inflexionWidth/2, y-inflexionWidth/2, inflexionWidth,inflexionWidth);
  237 + CGPoint circleCenter = CGPointMake(circleRect.origin.x + (circleRect.size.width / 2), circleRect.origin.y + (circleRect.size.height / 2));
  238 +
  239 + [pointPath moveToPoint:CGPointMake(circleCenter.x + (inflexionWidth/2), circleCenter.y)];
  240 + [pointPath addArcWithCenter:circleCenter radius:inflexionWidth/2 startAngle:0 endAngle:2*M_PI clockwise:YES];
  241 +
  242 + if ( i != 0 ) {
  243 +
  244 + // calculate the point for line
  245 + float distance = sqrt( pow(x-last_x, 2) + pow(y-last_y,2) );
  246 + float last_x1 = last_x + (inflexionWidth/2) / distance * (x-last_x);
  247 + float last_y1 = last_y + (inflexionWidth/2) / distance * (y-last_y);
  248 + float x1 = x - (inflexionWidth/2) / distance * (x-last_x);
  249 + float y1 = y - (inflexionWidth/2) / distance * (y-last_y);
  250 +
  251 + [progressline moveToPoint:CGPointMake(last_x1, last_y1)];
  252 + [progressline addLineToPoint:CGPointMake(x1, y1)];
  253 + }
  254 +
  255 + last_x = x;
  256 + last_y = y;
219 } 257 }
220 - 258 + // Square style point
221 - [progressline moveToPoint:point]; 259 + else if (chartData.inflexionPointStyle == PNLineChartPointStyleSquare) {
222 - [linePointsArray addObject:[NSValue valueWithCGPoint:point]]; 260 +
  261 + CGRect squareRect = CGRectMake(x-inflexionWidth/2, y-inflexionWidth/2, inflexionWidth,inflexionWidth);
  262 + CGPoint squareCenter = CGPointMake(squareRect.origin.x + (squareRect.size.width / 2), squareRect.origin.y + (squareRect.size.height / 2));
  263 +
  264 + [pointPath moveToPoint:CGPointMake(squareCenter.x - (inflexionWidth/2), squareCenter.y - (inflexionWidth/2))];
  265 + [pointPath addLineToPoint:CGPointMake(squareCenter.x + (inflexionWidth/2), squareCenter.y - (inflexionWidth/2))];
  266 + [pointPath addLineToPoint:CGPointMake(squareCenter.x + (inflexionWidth/2), squareCenter.y + (inflexionWidth/2))];
  267 + [pointPath addLineToPoint:CGPointMake(squareCenter.x - (inflexionWidth/2), squareCenter.y + (inflexionWidth/2))];
  268 + [pointPath closePath];
  269 +
  270 + if ( i != 0 ) {
  271 +
  272 + // calculate the point for line
  273 + float distance = sqrt( pow(x-last_x, 2) + pow(y-last_y,2) );
  274 + float last_x1 = last_x + (inflexionWidth/2);
  275 + float last_y1 = last_y + (inflexionWidth/2) / distance * (y-last_y);
  276 + float x1 = x - (inflexionWidth/2);
  277 + float y1 = y - (inflexionWidth/2) / distance * (y-last_y);
  278 +
  279 + [progressline moveToPoint:CGPointMake(last_x1, last_y1)];
  280 + [progressline addLineToPoint:CGPointMake(x1, y1)];
  281 + }
  282 +
  283 + last_x = x;
  284 + last_y = y;
  285 + }
  286 + // Triangle style point
  287 + else if (chartData.inflexionPointStyle == PNLineChartPointStyleTriangle) {
  288 +
  289 + if ( i != 0 ) {
  290 + [progressline addLineToPoint:CGPointMake(x, y)];
  291 + }
  292 +
  293 + [progressline moveToPoint:CGPointMake(x, y)];
  294 + } else {
  295 +
  296 + if ( i != 0 ) {
  297 + [progressline addLineToPoint:CGPointMake(x, y)];
  298 + }
  299 +
  300 + [progressline moveToPoint:CGPointMake(x, y)];
  301 + }
  302 +
  303 + [linePointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(x, y)]];
223 } 304 }
224 305
225 [_pathPoints addObject:[linePointsArray copy]]; 306 [_pathPoints addObject:[linePointsArray copy]];
@@ -227,29 +308,42 @@ @@ -227,29 +308,42 @@
227 // setup the color of the chart line 308 // setup the color of the chart line
228 if (chartData.color) { 309 if (chartData.color) {
229 chartLine.strokeColor = [chartData.color CGColor]; 310 chartLine.strokeColor = [chartData.color CGColor];
  311 + pointLayer.strokeColor = [chartData.color CGColor];
230 } 312 }
231 else { 313 else {
232 chartLine.strokeColor = [PNGreen CGColor]; 314 chartLine.strokeColor = [PNGreen CGColor];
  315 + pointLayer.strokeColor = [PNGreen CGColor];
233 } 316 }
234 317
235 [progressline stroke]; 318 [progressline stroke];
236 319
237 chartLine.path = progressline.CGPath; 320 chartLine.path = progressline.CGPath;
238 - 321 + pointLayer.path = pointPath.CGPath;
  322 +
  323 +
  324 + [CATransaction begin];
239 CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 325 CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
240 pathAnimation.duration = 1.0; 326 pathAnimation.duration = 1.0;
241 pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 327 pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
242 pathAnimation.fromValue = @0.0f; 328 pathAnimation.fromValue = @0.0f;
243 pathAnimation.toValue = @1.0f; 329 pathAnimation.toValue = @1.0f;
244 - [chartLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"];  
245 330
  331 + [chartLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
246 chartLine.strokeEnd = 1.0; 332 chartLine.strokeEnd = 1.0;
  333 +
  334 + if (chartData.inflexionPointStyle == PNLineChartPointStyleCycle) {
  335 + [pointLayer addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
  336 + }
  337 +
  338 + [CATransaction setCompletionBlock:^{
  339 + //pointLayer.strokeEnd = 1.0f; // stroken point when animation end
  340 + }];
  341 + [CATransaction commit];
247 342
248 UIGraphicsEndImageContext(); 343 UIGraphicsEndImageContext();
249 } 344 }
250 } 345 }
251 346
252 -  
253 - (void)setChartData:(NSArray *)data 347 - (void)setChartData:(NSArray *)data
254 { 348 {
255 if (data != _chartData) { 349 if (data != _chartData) {
@@ -262,20 +356,38 @@ @@ -262,20 +356,38 @@
262 for (CALayer *layer in self.chartLineArray) { 356 for (CALayer *layer in self.chartLineArray) {
263 [layer removeFromSuperlayer]; 357 [layer removeFromSuperlayer];
264 } 358 }
  359 + for (CALayer *layer in self.chartPointArray) {
  360 + [layer removeFromSuperlayer];
  361 + }
265 362
266 self.chartLineArray = [NSMutableArray arrayWithCapacity:data.count]; 363 self.chartLineArray = [NSMutableArray arrayWithCapacity:data.count];
  364 + self.chartPointArray = [NSMutableArray arrayWithCapacity:data.count];
267 365
  366 + // set for point stoken
  367 + float circle_stroke_width = 2.f;
  368 + float line_width = 3.0f;
  369 +
268 for (PNLineChartData *chartData in data) { 370 for (PNLineChartData *chartData in data) {
269 // create as many chart line layers as there are data-lines 371 // create as many chart line layers as there are data-lines
270 CAShapeLayer *chartLine = [CAShapeLayer layer]; 372 CAShapeLayer *chartLine = [CAShapeLayer layer];
271 - chartLine.lineCap = kCALineCapRound; 373 + chartLine.lineCap = kCALineCapButt;
272 - chartLine.lineJoin = kCALineJoinBevel; 374 + chartLine.lineJoin = kCALineJoinMiter;
273 - chartLine.fillColor = [[UIColor whiteColor] CGColor]; 375 + chartLine.fillColor = [[UIColor whiteColor] CGColor];
274 - chartLine.lineWidth = 3.0; 376 + chartLine.lineWidth = line_width;
275 - chartLine.strokeEnd = 0.0; 377 + chartLine.strokeEnd = 0.0;
276 [self.layer addSublayer:chartLine]; 378 [self.layer addSublayer:chartLine];
277 [self.chartLineArray addObject:chartLine]; 379 [self.chartLineArray addObject:chartLine];
278 380
  381 + // create point
  382 + CAShapeLayer *pointLayer = [CAShapeLayer layer];
  383 + pointLayer.strokeColor = [chartData.color CGColor];
  384 + pointLayer.lineCap = kCALineCapRound;
  385 + pointLayer.lineJoin = kCALineJoinBevel;
  386 + pointLayer.fillColor = nil;
  387 + pointLayer.lineWidth = circle_stroke_width;
  388 + [self.layer addSublayer:pointLayer];
  389 + [self.chartPointArray addObject:pointLayer];
  390 +
279 for (NSUInteger i = 0; i < chartData.itemCount; i++) { 391 for (NSUInteger i = 0; i < chartData.itemCount; i++) {
280 yValue = chartData.getData(i).y; 392 yValue = chartData.getData(i).y;
281 [yLabelsArray addObject:[NSString stringWithFormat:@"%2f", yValue]]; 393 [yLabelsArray addObject:[NSString stringWithFormat:@"%2f", yValue]];
@@ -385,7 +497,6 @@ @@ -385,7 +497,6 @@
385 + (float)heightOfString:(NSString *)text withWidth:(float)width font:(UIFont *)font 497 + (float)heightOfString:(NSString *)text withWidth:(float)width font:(UIFont *)font
386 { 498 {
387 NSInteger ch; 499 NSInteger ch;
388 - //设置字体  
389 CGSize size = CGSizeMake(width, MAXFLOAT); 500 CGSize size = CGSizeMake(width, MAXFLOAT);
390 if ([text respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) 501 if ([text respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)])
391 { 502 {
@@ -399,7 +510,7 @@ @@ -399,7 +510,7 @@
399 { 510 {
400 #pragma clang diagnostic push 511 #pragma clang diagnostic push
401 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 512 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
402 - size = [text sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByCharWrapping]; //ios7以上已经摒弃的这个方法 513 + size = [text sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByCharWrapping];
403 #pragma clang diagnostic pop 514 #pragma clang diagnostic pop
404 } 515 }
405 ch = size.height; 516 ch = size.height;
@@ -5,14 +5,36 @@ @@ -5,14 +5,36 @@
5 5
6 #import <Foundation/Foundation.h> 6 #import <Foundation/Foundation.h>
7 7
  8 +/**
  9 + * not support PNLineChartPointStyleTriangle style recently
  10 + */
  11 +typedef NS_ENUM(NSUInteger, PNLineChartPointStyle) {
  12 +
  13 + PNLineChartPointStyleNone = 0,
  14 + PNLineChartPointStyleCycle,
  15 + PNLineChartPointStyleTriangle,
  16 + PNLineChartPointStyleSquare
  17 +};
  18 +
8 @class PNLineChartDataItem; 19 @class PNLineChartDataItem;
9 20
10 typedef PNLineChartDataItem *(^LCLineChartDataGetter)(NSUInteger item); 21 typedef PNLineChartDataItem *(^LCLineChartDataGetter)(NSUInteger item);
11 22
12 -  
13 @interface PNLineChartData : NSObject 23 @interface PNLineChartData : NSObject
14 24
15 @property (strong) UIColor *color; 25 @property (strong) UIColor *color;
16 @property NSUInteger itemCount; 26 @property NSUInteger itemCount;
17 @property (copy) LCLineChartDataGetter getData; 27 @property (copy) LCLineChartDataGetter getData;
  28 +
  29 +@property (nonatomic, assign) PNLineChartPointStyle inflexionPointStyle;
  30 +
  31 +/**
  32 + * if PNLineChartPointStyle is cycle, inflexionPointWidth equal cycle's diameter
  33 + * if PNLineChartPointStyle is square, that means the foundation is square with
  34 + * inflexionPointWidth long
  35 + */
  36 +@property (nonatomic, assign) CGFloat inflexionPointWidth;
  37 +
  38 +@property (nonatomic, assign) CGFloat lineWidth;
  39 +
18 @end 40 @end
@@ -7,4 +7,22 @@ @@ -7,4 +7,22 @@
7 7
8 @implementation PNLineChartData 8 @implementation PNLineChartData
9 9
  10 +- (id)init
  11 +{
  12 + self = [super init];
  13 + if (self) {
  14 +
  15 + [self setDefaultValues];
  16 + }
  17 +
  18 + return self;
  19 +}
  20 +
  21 +- (void)setDefaultValues
  22 +{
  23 + _inflexionPointStyle = PNLineChartPointStyleNone;
  24 + _inflexionPointWidth = 6.f;
  25 + _lineWidth = 2.f;
  26 +}
  27 +
10 @end 28 @end
@@ -64,12 +64,14 @@ @@ -64,12 +64,14 @@
64 lineChart.yLabelFormat = @"%1.1f"; 64 lineChart.yLabelFormat = @"%1.1f";
65 lineChart.backgroundColor = [UIColor clearColor]; 65 lineChart.backgroundColor = [UIColor clearColor];
66 [lineChart setXLabels:@[@"SEP 1",@"SEP 2",@"SEP 3",@"SEP 4",@"SEP 5",@"SEP 6",@"SEP 7"]]; 66 [lineChart setXLabels:@[@"SEP 1",@"SEP 2",@"SEP 3",@"SEP 4",@"SEP 5",@"SEP 6",@"SEP 7"]];
  67 + lineChart.showCoordinateAxis = YES;
67 68
68 // Line Chart Nr.1 69 // Line Chart Nr.1
69 NSArray * data01Array = @[@60.1, @160.1, @126.4, @262.2, @186.2, @127.2, @176.2]; 70 NSArray * data01Array = @[@60.1, @160.1, @126.4, @262.2, @186.2, @127.2, @176.2];
70 PNLineChartData *data01 = [PNLineChartData new]; 71 PNLineChartData *data01 = [PNLineChartData new];
71 data01.color = PNFreshGreen; 72 data01.color = PNFreshGreen;
72 data01.itemCount = lineChart.xLabels.count; 73 data01.itemCount = lineChart.xLabels.count;
  74 + data01.inflexionPointStyle = PNLineChartPointStyleCycle;
73 data01.getData = ^(NSUInteger index) { 75 data01.getData = ^(NSUInteger index) {
74 CGFloat yValue = [data01Array[index] floatValue]; 76 CGFloat yValue = [data01Array[index] floatValue];
75 return [PNLineChartDataItem dataItemWithY:yValue]; 77 return [PNLineChartDataItem dataItemWithY:yValue];
@@ -80,6 +82,7 @@ @@ -80,6 +82,7 @@
80 PNLineChartData *data02 = [PNLineChartData new]; 82 PNLineChartData *data02 = [PNLineChartData new];
81 data02.color = PNTwitterColor; 83 data02.color = PNTwitterColor;
82 data02.itemCount = lineChart.xLabels.count; 84 data02.itemCount = lineChart.xLabels.count;
  85 + data02.inflexionPointStyle = PNLineChartPointStyleSquare;
83 data02.getData = ^(NSUInteger index) { 86 data02.getData = ^(NSUInteger index) {
84 CGFloat yValue = [data02Array[index] floatValue]; 87 CGFloat yValue = [data02Array[index] floatValue];
85 return [PNLineChartDataItem dataItemWithY:yValue]; 88 return [PNLineChartDataItem dataItemWithY:yValue];