// // PNCircleChart.m // PNChartDemo // // Created by kevinzhow on 13-11-30. // Copyright (c) 2013年 kevinzhow. All rights reserved. // #import "PNCircleChart.h" @implementation PNCircleChart // //- (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise { // // return [self initWithFrame:frame // total:total // current:current // clockwise:clockwise // shadow:NO // shadowColor:[UIColor clearColor] // displayCountingLabel:YES // overrideLineWidth:@8.0f]; // //} // //- (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise shadow:(BOOL)hasBackgroundShadow shadowColor:(UIColor *)backgroundShadowColor { // // return [self initWithFrame:frame // total:total // current:current // clockwise:clockwise // shadow:shadow // shadowColor:backgroundShadowColor // displayCountingLabel:YES // overrideLineWidth:@8.0f]; // //} // //- (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise shadow:(BOOL)hasBackgroundShadow shadowColor:(UIColor *)backgroundShadowColor displayCountingLabel:(BOOL)displayCountingLabel { // // return [self initWithFrame:frame // total:total // current:current // clockwise:clockwise // shadow:shadow // shadowColor:backgroundShadowColor // displayCountingLabel:displayCountingLabel // overrideLineWidth:@8.0f]; // //} - (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise shadow:(BOOL)hasBackgroundShadow shadowColor:(UIColor *)backgroundShadowColor displayCountingLabel:(BOOL)displayCountingLabel overrideLineWidth:(NSNumber *)overrideLineWidth strokeColor:(UIColor *)strokeColor { self = [super initWithFrame:frame]; if (self) { _total = total; _current = current; _strokeColor = strokeColor; _duration = 1.0; _chartType = PNChartFormatTypePercent; _displayAnimated = YES; _displayCountingLabel = displayCountingLabel; CGFloat startAngle = clockwise ? -90.0f : 270.0f; CGFloat endAngle = clockwise ? -90.01f : 270.01f; _lineWidth = overrideLineWidth; UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width/2.0f, self.frame.size.height/2.0f) radius:(self.frame.size.height * 0.5) - ([_lineWidth floatValue]/2.0f) startAngle:DEGREES_TO_RADIANS(startAngle) endAngle:DEGREES_TO_RADIANS(endAngle) clockwise:clockwise]; _circle = [CAShapeLayer layer]; _circle.path = circlePath.CGPath; _circle.lineCap = kCALineCapRound; _circle.fillColor = [UIColor clearColor].CGColor; _circle.lineWidth = [_lineWidth floatValue]; _circle.zPosition = 1; _circleBackground = [CAShapeLayer layer]; _circleBackground.path = circlePath.CGPath; _circleBackground.lineCap = kCALineCapRound; _circleBackground.fillColor = [UIColor clearColor].CGColor; _circleBackground.lineWidth = [_lineWidth floatValue]; _circleBackground.strokeColor = (hasBackgroundShadow ? backgroundShadowColor.CGColor : [UIColor clearColor].CGColor); _circleBackground.strokeEnd = 1.0; _circleBackground.zPosition = -1; [self.layer addSublayer:_circle]; [self.layer addSublayer:_circleBackground]; _countingLabel = [[UICountingLabel alloc] initWithFrame:CGRectMake(0, 0, 45, 30.0)]; [_countingLabel setTextAlignment:NSTextAlignmentCenter]; [_countingLabel setFont:[UIFont boldSystemFontOfSize:17.0f]]; [_countingLabel setTextColor:[UIColor grayColor]]; [_countingLabel setBackgroundColor:[UIColor clearColor]]; [_countingLabel setCenter:CGPointMake(self.frame.size.width/2.0f, self.frame.size.height/2.0f)]; _countingLabel.method = UILabelCountingMethodEaseInOut; if (_displayCountingLabel) { [self addSubview:_countingLabel]; } } return self; } - (void)strokeChart { // Add counting label if (_displayCountingLabel) { NSString *format; switch (self.chartType) { case PNChartFormatTypePercent: format = @"%d%%"; break; case PNChartFormatTypeDollar: format = @"$%d"; break; case PNChartFormatTypeDecimal: format = @"%.1f"; break; case PNChartFormatTypeDecimalTwoPlaces: format = @"%.2f"; break; case PNChartFormatTypeNone: default: format = @"%d"; break; } self.countingLabel.format = format; [self addSubview:self.countingLabel]; } // Add circle params _circle.lineWidth = [_lineWidth floatValue]; _circleBackground.lineWidth = [_lineWidth floatValue]; _circleBackground.strokeEnd = 1.0; _circle.strokeColor = _strokeColor.CGColor; _circle.strokeEnd = [_current floatValue] / [_total floatValue]; // Check if user wants to add a gradient from the start color to the bar color if (_strokeColorGradientStart) { // Add gradient self.gradientMask = [CAShapeLayer layer]; self.gradientMask.fillColor = [[UIColor clearColor] CGColor]; self.gradientMask.strokeColor = [[UIColor blackColor] CGColor]; self.gradientMask.lineWidth = _circle.lineWidth; self.gradientMask.lineCap = kCALineCapRound; CGRect gradientFrame = CGRectMake(0, 0, 2*self.bounds.size.width, 2*self.bounds.size.height); self.gradientMask.frame = gradientFrame; self.gradientMask.path = _circle.path; CAGradientLayer *gradientLayer = [CAGradientLayer layer]; gradientLayer.startPoint = CGPointMake(0.5,1.0); gradientLayer.endPoint = CGPointMake(0.5,0.0); gradientLayer.frame = gradientFrame; UIColor *endColor = (_strokeColor ? _strokeColor : [UIColor greenColor]); NSArray *colors = @[ (id)endColor.CGColor, (id)_strokeColorGradientStart.CGColor ]; gradientLayer.colors = colors; [gradientLayer setMask:self.gradientMask]; [_circle addSublayer:gradientLayer]; self.gradientMask.strokeEnd = [_current floatValue] / [_total floatValue]; } [self addAnimationIfNeeded]; } - (void)growChartByAmount:(NSNumber *)growAmount { NSNumber *updatedValue = [NSNumber numberWithFloat:[_current floatValue] + [growAmount floatValue]]; // Add animation [self updateChartByCurrent:updatedValue]; } -(void)updateChartByCurrent:(NSNumber *)current{ [self updateChartByCurrent:current byTotal:_total]; } -(void)updateChartByCurrent:(NSNumber *)current byTotal:(NSNumber *)total { double totalPercentageValue = [current floatValue]/([total floatValue]/100.0); if (_strokeColorGradientStart) { self.gradientMask.strokeEnd = _circle.strokeEnd; } // Add animation if (self.displayAnimated) { CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; pathAnimation.duration = self.duration; pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; pathAnimation.fromValue = @([_current floatValue] / [_total floatValue]); pathAnimation.toValue = @([current floatValue] / [total floatValue]); if (_strokeColorGradientStart) { [self.gradientMask addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; } [_circle addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; if (_displayCountingLabel) { [self.countingLabel countFrom:fmin([_current floatValue], [_total floatValue]) to:totalPercentageValue withDuration:self.duration]; } } else if (_displayCountingLabel) { [self.countingLabel countFrom:totalPercentageValue to:totalPercentageValue withDuration:self.duration]; } _circle.strokeEnd = [current floatValue] / [total floatValue]; _current = current; _total = total; } - (void)addAnimationIfNeeded { double percentageValue = (_current.floatValue / _total.floatValue) * 100.0f; if (self.displayAnimated) { CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; pathAnimation.duration = self.duration; pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; pathAnimation.fromValue = @(0.0f); pathAnimation.toValue = @([_current floatValue] / [_total floatValue]); [_circle addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; if(_displayCountingLabel) { [_countingLabel countFrom:0 to:percentageValue withDuration:self.duration]; } if (self.gradientMask && _strokeColorGradientStart) { [self.gradientMask addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; } } else { if (_displayCountingLabel) { [_countingLabel countFrom:percentageValue to:percentageValue withDuration:self.duration]; } } } @end