Zhang Hang

PieChart divides equally when all value of data items are 0

@@ -11,8 +11,7 @@ @@ -11,8 +11,7 @@
11 @interface PNPieChart() 11 @interface PNPieChart()
12 12
13 @property (nonatomic, readwrite) NSArray *items; 13 @property (nonatomic, readwrite) NSArray *items;
14 -@property (nonatomic) CGFloat total; 14 +@property (nonatomic) NSArray *endPercentages;
15 -@property (nonatomic) CGFloat currentTotal;  
16 15
17 @property (nonatomic) CGFloat outerCircleRadius; 16 @property (nonatomic) CGFloat outerCircleRadius;
18 @property (nonatomic) CGFloat innerCircleRadius; 17 @property (nonatomic) CGFloat innerCircleRadius;
@@ -21,11 +20,14 @@ @@ -21,11 +20,14 @@
21 @property (nonatomic) CAShapeLayer *pieLayer; 20 @property (nonatomic) CAShapeLayer *pieLayer;
22 @property (nonatomic) NSMutableArray *descriptionLabels; 21 @property (nonatomic) NSMutableArray *descriptionLabels;
23 22
  23 +
24 - (void)loadDefault; 24 - (void)loadDefault;
25 25
26 - (UILabel *)descriptionLabelForItemAtIndex:(NSUInteger)index; 26 - (UILabel *)descriptionLabelForItemAtIndex:(NSUInteger)index;
27 - (PNPieChartDataItem *)dataItemForIndex:(NSUInteger)index; 27 - (PNPieChartDataItem *)dataItemForIndex:(NSUInteger)index;
28 - 28 +- (CGFloat)startPercentageForItemAtIndex:(NSUInteger)index;
  29 +- (CGFloat)endPercentageForItemAtIndex:(NSUInteger)index;
  30 +- (CGFloat)ratioForItemAtIndex:(NSUInteger)index;
29 - (CAShapeLayer *)newCircleLayerWithRadius:(CGFloat)radius 31 - (CAShapeLayer *)newCircleLayerWithRadius:(CGFloat)radius
30 borderWidth:(CGFloat)borderWidth 32 borderWidth:(CGFloat)borderWidth
31 fillColor:(UIColor *)fillColor 33 fillColor:(UIColor *)fillColor
@@ -40,97 +42,94 @@ @@ -40,97 +42,94 @@
40 @implementation PNPieChart 42 @implementation PNPieChart
41 43
42 -(id)initWithFrame:(CGRect)frame items:(NSArray *)items{ 44 -(id)initWithFrame:(CGRect)frame items:(NSArray *)items{
43 - self = [self initWithFrame:frame]; 45 + self = [self initWithFrame:frame];
44 - if(self){ 46 + if(self){
45 - _items = [NSArray arrayWithArray:items]; 47 + _items = [NSArray arrayWithArray:items];
46 - _outerCircleRadius = CGRectGetWidth(self.bounds)/2; 48 + _outerCircleRadius = CGRectGetWidth(self.bounds)/2;
47 - _innerCircleRadius = CGRectGetWidth(self.bounds)/6; 49 + _innerCircleRadius = CGRectGetWidth(self.bounds)/6;
48 - 50 +
49 - _descriptionTextColor = [UIColor whiteColor]; 51 + _descriptionTextColor = [UIColor whiteColor];
50 - _descriptionTextFont = [UIFont fontWithName:@"Avenir-Medium" size:18.0]; 52 + _descriptionTextFont = [UIFont fontWithName:@"Avenir-Medium" size:18.0];
51 _descriptionTextShadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; 53 _descriptionTextShadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
52 _descriptionTextShadowOffset = CGSizeMake(0, 1); 54 _descriptionTextShadowOffset = CGSizeMake(0, 1);
53 - _duration = 1.0; 55 + _duration = 1.0;
54 56
55 - [self loadDefault]; 57 + [self loadDefault];
56 - } 58 + }
57 - 59 +
58 - return self; 60 + return self;
59 } 61 }
60 62
61 -  
62 - (void)loadDefault{ 63 - (void)loadDefault{
63 - _currentTotal = 0; 64 + __block CGFloat currentTotal = 0;
64 - _total = 0; 65 + CGFloat total = [[self.items valueForKeyPath:@"@sum.value"] floatValue];
65 - 66 + NSMutableArray *endPercentages = [NSMutableArray new];
66 - [_contentView removeFromSuperview]; 67 + [_items enumerateObjectsUsingBlock:^(PNPieChartDataItem *item, NSUInteger idx, BOOL *stop) {
67 - _contentView = [[UIView alloc] initWithFrame:self.bounds]; 68 + if (total == 0){
68 - [self addSubview:_contentView]; 69 + [endPercentages addObject:@(1.0/_items.count*(idx+1))];
69 - [_descriptionLabels removeAllObjects]; 70 + }else{
70 - _descriptionLabels = [NSMutableArray new]; 71 + currentTotal += item.value;
71 - 72 + [endPercentages addObject:@(currentTotal/total)];
72 - _pieLayer = [CAShapeLayer layer]; 73 + }
73 - [_contentView.layer addSublayer:_pieLayer]; 74 + }];
  75 + self.endPercentages = [endPercentages copy];
  76 +
  77 + [_contentView removeFromSuperview];
  78 + _contentView = [[UIView alloc] initWithFrame:self.bounds];
  79 + [self addSubview:_contentView];
  80 + _descriptionLabels = [NSMutableArray new];
  81 +
  82 + _pieLayer = [CAShapeLayer layer];
  83 + [_contentView.layer addSublayer:_pieLayer];
74 } 84 }
75 85
76 #pragma mark - 86 #pragma mark -
77 87
78 - (void)strokeChart{ 88 - (void)strokeChart{
79 - [self loadDefault]; 89 + [self loadDefault];
80 - 90 +
81 - [self.items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 91 + PNPieChartDataItem *currentItem;
82 - _total +=((PNPieChartDataItem *)obj).value; 92 + for (int i = 0; i < _items.count; i++) {
83 - }]; 93 + currentItem = [self dataItemForIndex:i];
84 - 94 +
85 - PNPieChartDataItem *currentItem; 95 +
86 - CGFloat currentValue = 0; 96 + CGFloat startPercnetage = [self startPercentageForItemAtIndex:i];
87 - for (int i = 0; i < _items.count; i++) { 97 + CGFloat endPercentage = [self endPercentageForItemAtIndex:i];
88 - currentItem = [self dataItemForIndex:i]; 98 +
89 - 99 + CGFloat radius = _innerCircleRadius + (_outerCircleRadius - _innerCircleRadius)/2;
90 - 100 + CGFloat borderWidth = _outerCircleRadius - _innerCircleRadius;
91 - CGFloat startPercnetage = currentValue/_total; 101 + CAShapeLayer *currentPieLayer = [self newCircleLayerWithRadius:radius
92 - CGFloat endPercentage = (currentValue + currentItem.value)/_total; 102 + borderWidth:borderWidth
93 -  
94 - CAShapeLayer *currentPieLayer = [self newCircleLayerWithRadius:_innerCircleRadius + (_outerCircleRadius - _innerCircleRadius)/2  
95 - borderWidth:_outerCircleRadius - _innerCircleRadius  
96 fillColor:[UIColor clearColor] 103 fillColor:[UIColor clearColor]
97 borderColor:currentItem.color 104 borderColor:currentItem.color
98 startPercentage:startPercnetage 105 startPercentage:startPercnetage
99 endPercentage:endPercentage]; 106 endPercentage:endPercentage];
100 - [_pieLayer addSublayer:currentPieLayer]; 107 + [_pieLayer addSublayer:currentPieLayer];
101 - 108 + }
102 - currentValue+=currentItem.value; 109 +
103 - 110 + [self maskChart];
104 - } 111 +
105 -  
106 - [self maskChart];  
107 -  
108 - currentValue = 0;  
109 for (int i = 0; i < _items.count; i++) { 112 for (int i = 0; i < _items.count; i++) {
110 - currentItem = [self dataItemForIndex:i]; 113 + UILabel *descriptionLabel = [self descriptionLabelForItemAtIndex:i];
111 - UILabel *descriptionLabel = [self descriptionLabelForItemAtIndex:i]; 114 + [_contentView addSubview:descriptionLabel];
112 - [_contentView addSubview:descriptionLabel];  
113 - currentValue+=currentItem.value;  
114 [_descriptionLabels addObject:descriptionLabel]; 115 [_descriptionLabels addObject:descriptionLabel];
115 - } 116 + }
116 } 117 }
117 118
118 - (UILabel *)descriptionLabelForItemAtIndex:(NSUInteger)index{ 119 - (UILabel *)descriptionLabelForItemAtIndex:(NSUInteger)index{
119 - PNPieChartDataItem *currentDataItem = [self dataItemForIndex:index]; 120 + PNPieChartDataItem *currentDataItem = [self dataItemForIndex:index];
120 CGFloat distance = _innerCircleRadius + (_outerCircleRadius - _innerCircleRadius) / 2; 121 CGFloat distance = _innerCircleRadius + (_outerCircleRadius - _innerCircleRadius) / 2;
121 - CGFloat centerPercentage =(_currentTotal + currentDataItem.value /2 ) / _total; 122 + CGFloat centerPercentage = ([self startPercentageForItemAtIndex:index] + [self endPercentageForItemAtIndex:index])/2;
122 CGFloat rad = centerPercentage * 2 * M_PI; 123 CGFloat rad = centerPercentage * 2 * M_PI;
123 124
124 - _currentTotal += currentDataItem.value;  
125 -  
126 UILabel *descriptionLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 80)]; 125 UILabel *descriptionLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 80)];
127 NSString *titleText = currentDataItem.textDescription; 126 NSString *titleText = currentDataItem.textDescription;
128 if(!titleText){ 127 if(!titleText){
129 - titleText = [NSString stringWithFormat:@"%.0f%%",currentDataItem.value/ _total * 100]; 128 + titleText = [NSString stringWithFormat:@"%.0f%%",[self ratioForItemAtIndex:index] * 100];
130 descriptionLabel.text = titleText ; 129 descriptionLabel.text = titleText ;
131 } 130 }
132 else { 131 else {
133 - NSString* str = [NSString stringWithFormat:@"%.0f%%\n",currentDataItem.value/ _total * 100]; 132 + NSString* str = [NSString stringWithFormat:@"%.0f%%\n",[self ratioForItemAtIndex:index] * 100];
134 str = [str stringByAppendingString:titleText]; 133 str = [str stringByAppendingString:titleText];
135 descriptionLabel.text = str ; 134 descriptionLabel.text = str ;
136 } 135 }
@@ -141,8 +140,8 @@ @@ -141,8 +140,8 @@
141 descriptionLabel.font = _descriptionTextFont; 140 descriptionLabel.font = _descriptionTextFont;
142 CGSize labelSize = [descriptionLabel.text sizeWithAttributes:@{NSFontAttributeName:descriptionLabel.font}]; 141 CGSize labelSize = [descriptionLabel.text sizeWithAttributes:@{NSFontAttributeName:descriptionLabel.font}];
143 descriptionLabel.frame = CGRectMake( 142 descriptionLabel.frame = CGRectMake(
144 - descriptionLabel.frame.origin.x, descriptionLabel.frame.origin.y, 143 + descriptionLabel.frame.origin.x, descriptionLabel.frame.origin.y,
145 - descriptionLabel.frame.size.width, labelSize.height); 144 + descriptionLabel.frame.size.width, labelSize.height);
146 descriptionLabel.numberOfLines = 0; 145 descriptionLabel.numberOfLines = 0;
147 descriptionLabel.textColor = _descriptionTextColor; 146 descriptionLabel.textColor = _descriptionTextColor;
148 descriptionLabel.shadowColor = _descriptionTextShadowColor; 147 descriptionLabel.shadowColor = _descriptionTextShadowColor;
@@ -151,11 +150,27 @@ @@ -151,11 +150,27 @@
151 descriptionLabel.center = center; 150 descriptionLabel.center = center;
152 descriptionLabel.alpha = 0; 151 descriptionLabel.alpha = 0;
153 descriptionLabel.backgroundColor = [UIColor clearColor]; 152 descriptionLabel.backgroundColor = [UIColor clearColor];
154 - return descriptionLabel; 153 + return descriptionLabel;
155 } 154 }
156 155
157 - (PNPieChartDataItem *)dataItemForIndex:(NSUInteger)index{ 156 - (PNPieChartDataItem *)dataItemForIndex:(NSUInteger)index{
158 - return self.items[index]; 157 + return self.items[index];
  158 +}
  159 +
  160 +- (CGFloat)startPercentageForItemAtIndex:(NSUInteger)index{
  161 + if(index == 0){
  162 + return 0;
  163 + }
  164 +
  165 + return [_endPercentages[index - 1] floatValue];
  166 +}
  167 +
  168 +- (CGFloat)endPercentageForItemAtIndex:(NSUInteger)index{
  169 + return [_endPercentages[index] floatValue];
  170 +}
  171 +
  172 +- (CGFloat)ratioForItemAtIndex:(NSUInteger)index{
  173 + return [self endPercentageForItemAtIndex:index] - [self startPercentageForItemAtIndex:index];
159 } 174 }
160 175
161 #pragma mark private methods 176 #pragma mark private methods
@@ -183,27 +198,29 @@ @@ -183,27 +198,29 @@
183 circle.lineWidth = borderWidth; 198 circle.lineWidth = borderWidth;
184 circle.path = path.CGPath; 199 circle.path = path.CGPath;
185 200
186 - 201 +
187 - return circle; 202 + return circle;
188 } 203 }
189 204
190 - (void)maskChart{ 205 - (void)maskChart{
191 - CAShapeLayer *maskLayer = [self newCircleLayerWithRadius:_innerCircleRadius + (_outerCircleRadius - _innerCircleRadius)/2 206 + CGFloat radius = _innerCircleRadius + (_outerCircleRadius - _innerCircleRadius)/2;
192 - borderWidth:_outerCircleRadius - _innerCircleRadius 207 + CGFloat borderWidth = _outerCircleRadius - _innerCircleRadius;
  208 + CAShapeLayer *maskLayer = [self newCircleLayerWithRadius:radius
  209 + borderWidth:borderWidth
193 fillColor:[UIColor clearColor] 210 fillColor:[UIColor clearColor]
194 borderColor:[UIColor blackColor] 211 borderColor:[UIColor blackColor]
195 startPercentage:0 212 startPercentage:0
196 endPercentage:1]; 213 endPercentage:1];
197 - 214 +
198 - _pieLayer.mask = maskLayer; 215 + _pieLayer.mask = maskLayer;
199 - CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 216 + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
200 - animation.duration = _duration; 217 + animation.duration = _duration;
201 - animation.fromValue = @0; 218 + animation.fromValue = @0;
202 - animation.toValue = @1; 219 + animation.toValue = @1;
203 animation.delegate = self; 220 animation.delegate = self;
204 - animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 221 + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
205 - animation.removedOnCompletion = YES; 222 + animation.removedOnCompletion = YES;
206 - [maskLayer addAnimation:animation forKey:@"circleAnimation"]; 223 + [maskLayer addAnimation:animation forKey:@"circleAnimation"];
207 } 224 }
208 225
209 - (void)createArcAnimationForLayer:(CAShapeLayer *)layer ForKey:(NSString *)key fromValue:(NSNumber *)from toValue:(NSNumber *)to Delegate:(id)delegate 226 - (void)createArcAnimationForLayer:(CAShapeLayer *)layer ForKey:(NSString *)key fromValue:(NSNumber *)from toValue:(NSNumber *)to Delegate:(id)delegate