PNCircleChart.m 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. //
  2. // PNCircleChart.m
  3. // PNChartDemo
  4. //
  5. // Created by kevinzhow on 13-11-30.
  6. // Copyright (c) 2013年 kevinzhow. All rights reserved.
  7. //
  8. #import "PNCircleChart.h"
  9. @implementation PNCircleChart
  10. //
  11. //- (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise {
  12. //
  13. // return [self initWithFrame:frame
  14. // total:total
  15. // current:current
  16. // clockwise:clockwise
  17. // shadow:NO
  18. // shadowColor:[UIColor clearColor]
  19. // displayCountingLabel:YES
  20. // overrideLineWidth:@8.0f];
  21. //
  22. //}
  23. //
  24. //- (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise shadow:(BOOL)hasBackgroundShadow shadowColor:(UIColor *)backgroundShadowColor {
  25. //
  26. // return [self initWithFrame:frame
  27. // total:total
  28. // current:current
  29. // clockwise:clockwise
  30. // shadow:shadow
  31. // shadowColor:backgroundShadowColor
  32. // displayCountingLabel:YES
  33. // overrideLineWidth:@8.0f];
  34. //
  35. //}
  36. //
  37. //- (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise shadow:(BOOL)hasBackgroundShadow shadowColor:(UIColor *)backgroundShadowColor displayCountingLabel:(BOOL)displayCountingLabel {
  38. //
  39. // return [self initWithFrame:frame
  40. // total:total
  41. // current:current
  42. // clockwise:clockwise
  43. // shadow:shadow
  44. // shadowColor:backgroundShadowColor
  45. // displayCountingLabel:displayCountingLabel
  46. // overrideLineWidth:@8.0f];
  47. //
  48. //}
  49. - (id)initWithFrame:(CGRect)frame
  50. total:(NSNumber *)total
  51. current:(NSNumber *)current
  52. clockwise:(BOOL)clockwise
  53. shadow:(BOOL)hasBackgroundShadow
  54. shadowColor:(UIColor *)backgroundShadowColor
  55. displayCountingLabel:(BOOL)displayCountingLabel
  56. overrideLineWidth:(NSNumber *)overrideLineWidth
  57. strokeColor:(UIColor *)strokeColor
  58. {
  59. self = [super initWithFrame:frame];
  60. if (self) {
  61. _total = total;
  62. _current = current;
  63. _strokeColor = strokeColor;
  64. _duration = 1.0;
  65. _chartType = PNChartFormatTypePercent;
  66. _displayAnimated = YES;
  67. _displayCountingLabel = displayCountingLabel;
  68. CGFloat startAngle = clockwise ? -90.0f : 270.0f;
  69. CGFloat endAngle = clockwise ? -90.01f : 270.01f;
  70. _lineWidth = overrideLineWidth;
  71. UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width/2.0f, self.frame.size.height/2.0f)
  72. radius:(self.frame.size.height * 0.5) - ([_lineWidth floatValue]/2.0f)
  73. startAngle:DEGREES_TO_RADIANS(startAngle)
  74. endAngle:DEGREES_TO_RADIANS(endAngle)
  75. clockwise:clockwise];
  76. _circle = [CAShapeLayer layer];
  77. _circle.path = circlePath.CGPath;
  78. _circle.lineCap = kCALineCapRound;
  79. _circle.fillColor = [UIColor clearColor].CGColor;
  80. _circle.lineWidth = [_lineWidth floatValue];
  81. _circle.zPosition = 1;
  82. _circleBackground = [CAShapeLayer layer];
  83. _circleBackground.path = circlePath.CGPath;
  84. _circleBackground.lineCap = kCALineCapRound;
  85. _circleBackground.fillColor = [UIColor clearColor].CGColor;
  86. _circleBackground.lineWidth = [_lineWidth floatValue];
  87. _circleBackground.strokeColor = (hasBackgroundShadow ? backgroundShadowColor.CGColor : [UIColor clearColor].CGColor);
  88. _circleBackground.strokeEnd = 1.0;
  89. _circleBackground.zPosition = -1;
  90. [self.layer addSublayer:_circle];
  91. [self.layer addSublayer:_circleBackground];
  92. _countingLabel = [[UICountingLabel alloc] initWithFrame:CGRectMake(0, 0, 45, 30.0)];
  93. [_countingLabel setTextAlignment:NSTextAlignmentCenter];
  94. [_countingLabel setFont:[UIFont boldSystemFontOfSize:17.0f]];
  95. [_countingLabel setTextColor:[UIColor grayColor]];
  96. [_countingLabel setBackgroundColor:[UIColor clearColor]];
  97. [_countingLabel setCenter:CGPointMake(self.frame.size.width/2.0f, self.frame.size.height/2.0f)];
  98. _countingLabel.method = UILabelCountingMethodEaseInOut;
  99. if (_displayCountingLabel) {
  100. [self addSubview:_countingLabel];
  101. }
  102. }
  103. return self;
  104. }
  105. - (void)strokeChart
  106. {
  107. // Add counting label
  108. if (_displayCountingLabel) {
  109. NSString *format;
  110. switch (self.chartType) {
  111. case PNChartFormatTypePercent:
  112. format = @"%d%%";
  113. break;
  114. case PNChartFormatTypeDollar:
  115. format = @"$%d";
  116. break;
  117. case PNChartFormatTypeDecimal:
  118. format = @"%.1f";
  119. break;
  120. case PNChartFormatTypeDecimalTwoPlaces:
  121. format = @"%.2f";
  122. break;
  123. case PNChartFormatTypeNone:
  124. default:
  125. format = @"%d";
  126. break;
  127. }
  128. self.countingLabel.format = format;
  129. [self addSubview:self.countingLabel];
  130. }
  131. // Add circle params
  132. _circle.lineWidth = [_lineWidth floatValue];
  133. _circleBackground.lineWidth = [_lineWidth floatValue];
  134. _circleBackground.strokeEnd = 1.0;
  135. _circle.strokeColor = _strokeColor.CGColor;
  136. _circle.strokeEnd = [_current floatValue] / [_total floatValue];
  137. // Check if user wants to add a gradient from the start color to the bar color
  138. if (_strokeColorGradientStart) {
  139. // Add gradient
  140. self.gradientMask = [CAShapeLayer layer];
  141. self.gradientMask.fillColor = [[UIColor clearColor] CGColor];
  142. self.gradientMask.strokeColor = [[UIColor blackColor] CGColor];
  143. self.gradientMask.lineWidth = _circle.lineWidth;
  144. self.gradientMask.lineCap = kCALineCapRound;
  145. CGRect gradientFrame = CGRectMake(0, 0, 2*self.bounds.size.width, 2*self.bounds.size.height);
  146. self.gradientMask.frame = gradientFrame;
  147. self.gradientMask.path = _circle.path;
  148. CAGradientLayer *gradientLayer = [CAGradientLayer layer];
  149. gradientLayer.startPoint = CGPointMake(0.5,1.0);
  150. gradientLayer.endPoint = CGPointMake(0.5,0.0);
  151. gradientLayer.frame = gradientFrame;
  152. UIColor *endColor = (_strokeColor ? _strokeColor : [UIColor greenColor]);
  153. NSArray *colors = @[
  154. (id)endColor.CGColor,
  155. (id)_strokeColorGradientStart.CGColor
  156. ];
  157. gradientLayer.colors = colors;
  158. [gradientLayer setMask:self.gradientMask];
  159. [_circle addSublayer:gradientLayer];
  160. self.gradientMask.strokeEnd = [_current floatValue] / [_total floatValue];
  161. }
  162. [self addAnimationIfNeeded];
  163. }
  164. - (void)growChartByAmount:(NSNumber *)growAmount
  165. {
  166. NSNumber *updatedValue = [NSNumber numberWithFloat:[_current floatValue] + [growAmount floatValue]];
  167. // Add animation
  168. [self updateChartByCurrent:updatedValue];
  169. }
  170. -(void)updateChartByCurrent:(NSNumber *)current{
  171. [self updateChartByCurrent:current
  172. byTotal:_total];
  173. }
  174. -(void)updateChartByCurrent:(NSNumber *)current byTotal:(NSNumber *)total {
  175. double totalPercentageValue = [current floatValue]/([total floatValue]/100.0);
  176. if (_strokeColorGradientStart) {
  177. self.gradientMask.strokeEnd = _circle.strokeEnd;
  178. }
  179. // Add animation
  180. if (self.displayAnimated) {
  181. CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
  182. pathAnimation.duration = self.duration;
  183. pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
  184. pathAnimation.fromValue = @([_current floatValue] / [_total floatValue]);
  185. pathAnimation.toValue = @([current floatValue] / [total floatValue]);
  186. if (_strokeColorGradientStart) {
  187. [self.gradientMask addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
  188. }
  189. [_circle addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
  190. if (_displayCountingLabel) {
  191. [self.countingLabel countFrom:fmin([_current floatValue], [_total floatValue]) to:totalPercentageValue withDuration:self.duration];
  192. }
  193. }
  194. else if (_displayCountingLabel) {
  195. [self.countingLabel countFrom:totalPercentageValue to:totalPercentageValue withDuration:self.duration];
  196. }
  197. _circle.strokeEnd = [current floatValue] / [total floatValue];
  198. _current = current;
  199. _total = total;
  200. }
  201. - (void)addAnimationIfNeeded
  202. {
  203. double percentageValue = (_current.floatValue / _total.floatValue) * 100.0f;
  204. if (self.displayAnimated) {
  205. CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
  206. pathAnimation.duration = self.duration;
  207. pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
  208. pathAnimation.fromValue = @(0.0f);
  209. pathAnimation.toValue = @([_current floatValue] / [_total floatValue]);
  210. [_circle addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
  211. if(_displayCountingLabel)
  212. {
  213. [_countingLabel countFrom:0 to:percentageValue withDuration:self.duration];
  214. }
  215. if (self.gradientMask && _strokeColorGradientStart) {
  216. [self.gradientMask addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
  217. }
  218. }
  219. else {
  220. if (_displayCountingLabel) {
  221. [_countingLabel countFrom:percentageValue to:percentageValue withDuration:self.duration];
  222. }
  223. }
  224. }
  225. @end