Permalink
Browse files

add custom label text

  • Loading branch information...
1 parent f40c0fc commit 9bb14e7b373a96cc3dbb0a70b1aa6f5e7aa19292 @xyfeng committed May 21, 2012
Showing with 74 additions and 70 deletions.
  1. +1 −0 README.textile
  2. +1 −6 XYPieChart/XYPieChart.h
  3. +72 −64 XYPieChart/XYPieChart.m
View
1 README.textile
@@ -46,6 +46,7 @@ Implement Data Source Methods:
- (NSUInteger)numberOfSlicesInPieChart:(XYPieChart *)pieChart;
- (CGFloat)pieChart:(XYPieChart *)pieChart valueForSliceAtIndex:(NSUInteger)index;
- (UIColor *)pieChart:(XYPieChart *)pieChart colorForSliceAtIndex:(NSUInteger)index; //optional
+- (NSString *)pieChart:(XYPieChart *)pieChart textForSliceAtIndex:(NSUInteger)index; //optional
</pre>
Implement  Delegate Methods (OPTIONAL):
View
7 XYPieChart/XYPieChart.h
@@ -29,19 +29,15 @@
#import <UIKit/UIKit.h>
@class XYPieChart;
-
-#pragma mark - Data Source Protocol
-//Provide value and color for each slice
@protocol XYPieChartDataSource <NSObject>
@required
- (NSUInteger)numberOfSlicesInPieChart:(XYPieChart *)pieChart;
- (CGFloat)pieChart:(XYPieChart *)pieChart valueForSliceAtIndex:(NSUInteger)index;
@optional
- (UIColor *)pieChart:(XYPieChart *)pieChart colorForSliceAtIndex:(NSUInteger)index;
+- (NSString *)pieChart:(XYPieChart *)pieChart textForSliceAtIndex:(NSUInteger)index;
@end
-#pragma mark - Delegate Protocol
-//Method called before and after one slice get selected
@protocol XYPieChartDelegate <NSObject>
@optional
- (void)pieChart:(XYPieChart *)pieChart willSelectSliceAtIndex:(NSUInteger)index;
@@ -50,7 +46,6 @@
- (void)pieChart:(XYPieChart *)pieChart didDeselectSliceAtIndex:(NSUInteger)index;
@end
-#pragma mark - Pie Chart
@interface XYPieChart : UIView
@property(nonatomic, weak) id<XYPieChartDataSource> dataSource;
@property(nonatomic, weak) id<XYPieChartDelegate> delegate;
View
136 XYPieChart/XYPieChart.m
@@ -35,10 +35,12 @@ @interface SliceLayer : CAShapeLayer
@property (nonatomic, assign) double startAngle;
@property (nonatomic, assign) double endAngle;
@property (nonatomic, assign) BOOL isSelected;
+@property (nonatomic, strong) NSString *text;
- (void)createArcAnimationForKey:(NSString *)key fromValue:(NSNumber *)from toValue:(NSNumber *)to Delegate:(id)delegate;
@end
@implementation SliceLayer
+@synthesize text = _text;
@synthesize value = _value;
@synthesize percentage = _percentage;
@synthesize startAngle = _startAngle;
@@ -48,7 +50,7 @@ - (NSString*)description
{
return [NSString stringWithFormat:@"value:%f, percentage:%0.0f, start:%d, end:%d", _value, _percentage, _startAngle/M_PI*180, _endAngle/M_PI*180];
}
-+ (BOOL)needsDisplayForKey:(NSString *)key
++ (BOOL)needsDisplayForKey:(NSString *)key
{
if ([key isEqualToString:@"startAngle"] || [key isEqualToString:@"endAngle"]) {
return YES;
@@ -74,15 +76,15 @@ - (void)createArcAnimationForKey:(NSString *)key fromValue:(NSNumber *)from toVa
NSNumber *currentAngle = [[self presentationLayer] valueForKey:key];
if(!currentAngle) currentAngle = from;
[arcAnimation setFromValue:currentAngle];
- [arcAnimation setToValue:to];
+ [arcAnimation setToValue:to];
[arcAnimation setDelegate:delegate];
[arcAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
[self addAnimation:arcAnimation forKey:key];
[self setValue:to forKey:key];
}
@end
-@interface XYPieChart (Private)
+@interface XYPieChart (Private)
- (void)updateTimerFired:(NSTimer *)timer;
- (SliceLayer *)createSliceLayer;
- (CGSize)sizeThatFitsString:(NSString *)string;
@@ -95,7 +97,7 @@ @implementation XYPieChart
NSInteger _selectedSliceIndex;
//pie view, contains all slices
UIView *_pieView;
-
+
//animation control
NSTimer *_animationTimer;
NSMutableArray *_animations;
@@ -116,14 +118,14 @@ @implementation XYPieChart
@synthesize selectedSliceOffsetRadius = _selectedSliceOffsetRadius;
@synthesize showPercentage = _showPercentage;
-static CGPathRef CGPathCreateArc(CGPoint center, CGFloat radius, CGFloat startAngle, CGFloat endAngle)
+static CGPathRef CGPathCreateArc(CGPoint center, CGFloat radius, CGFloat startAngle, CGFloat endAngle)
{
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, center.x, center.y);
-
+
CGPathAddArc(path, NULL, center.x, center.y, radius, startAngle, endAngle, 0);
CGPathCloseSubpath(path);
-
+
return path;
}
@@ -136,20 +138,20 @@ - (id)initWithFrame:(CGRect)frame
_pieView = [[UIView alloc] initWithFrame:frame];
[_pieView setBackgroundColor:[UIColor clearColor]];
[self addSubview:_pieView];
-
+
_selectedSliceIndex = -1;
_animations = [[NSMutableArray alloc] init];
-
+
_animationSpeed = 0.5;
_startPieAngle = M_PI_2*3;
_selectedSliceStroke = 3.0;
-
+
self.pieRadius = MIN(frame.size.width/2, frame.size.height/2) - 10;
self.pieCenter = CGPointMake(frame.size.width/2, frame.size.height/2);
self.labelFont = [UIFont boldSystemFontOfSize:MAX((int)self.pieRadius/10, 5)];
_labelRadius = _pieRadius/2;
_selectedSliceOffsetRadius = MAX(10, _pieRadius/10);
-
+
_showLabel = YES;
_showPercentage = YES;
}
@@ -175,21 +177,21 @@ - (id)initWithCoder:(NSCoder *)aDecoder
_pieView = [[UIView alloc] initWithFrame:self.bounds];
[_pieView setBackgroundColor:[UIColor clearColor]];
[self insertSubview:_pieView atIndex:0];
-
+
_selectedSliceIndex = -1;
_animations = [[NSMutableArray alloc] init];
-
+
_animationSpeed = 0.5;
_startPieAngle = M_PI_2*3;
_selectedSliceStroke = 3.0;
-
+
CGRect bounds = [[self layer] bounds];
self.pieRadius = MIN(bounds.size.width/2, bounds.size.height/2) - 10;
self.pieCenter = CGPointMake(bounds.size.width/2, bounds.size.height/2);
self.labelFont = [UIFont boldSystemFontOfSize:MAX((int)self.pieRadius/10, 5)];
_labelRadius = _pieRadius/2;
_selectedSliceOffsetRadius = MAX(10, _pieRadius/10);
-
+
_showLabel = YES;
_showPercentage = YES;
}
@@ -230,9 +232,9 @@ - (void)setShowPercentage:(BOOL)showPercentage
if(_showPercentage)
label = [NSString stringWithFormat:@"%0.0f", layer.percentage*100];
else
- label = [NSString stringWithFormat:@"%0.0f", layer.value];
+ label = (layer.text)?layer.text:[NSString stringWithFormat:@"%0.0f", layer.value];
CGSize size = [label sizeWithFont:self.labelFont];
-
+
if(M_PI*2*_labelRadius*layer.percentage < MAX(size.width,size.height))
{
[textLayer setString:@""];
@@ -274,92 +276,92 @@ - (void)setSliceDeselectedAtIndex:(NSInteger)index
- (void)reloadData
{
- if (_dataSource && !_animationTimer)
+ if (_dataSource && !_animationTimer)
{
CALayer *parentLayer = [_pieView layer];
NSArray *slicelayers = [parentLayer sublayers];
-
+
_selectedSliceIndex = -1;
[slicelayers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
SliceLayer *layer = (SliceLayer *)obj;
if(layer.isSelected)
[self setSliceDeselectedAtIndex:idx];
}];
-
+
double startToAngle = 0.0;
double endToAngle = startToAngle;
-
+
NSUInteger sliceCount = [_dataSource numberOfSlicesInPieChart:self];
-
+
double sum = 0.0;
double values[sliceCount];
for (int index = 0; index < sliceCount; index++) {
values[index] = [_dataSource pieChart:self valueForSliceAtIndex:index];
sum += values[index];
}
-
+
double angles[sliceCount];
for (int index = 0; index < sliceCount; index++) {
double div;
if (sum == 0)
div = 0;
else
- div = values[index] / sum;
+ div = values[index] / sum;
angles[index] = M_PI * 2 * div;
}
[CATransaction begin];
[CATransaction setAnimationDuration:_animationSpeed];
-
+
[_pieView setUserInteractionEnabled:NO];
-
+
__block NSMutableArray *layersToRemove = nil;
[CATransaction setCompletionBlock:^{
-
+
[layersToRemove enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[obj removeFromSuperlayer];
}];
-
+
[layersToRemove removeAllObjects];
-
+
for(SliceLayer *layer in _pieView.layer.sublayers)
{
[layer setZPosition:kDefaultSliceZOrder];
}
-
+
[_pieView setUserInteractionEnabled:YES];
}];
-
+
BOOL isOnStart = ([slicelayers count] == 0 && sliceCount);
NSInteger diff = sliceCount - [slicelayers count];
layersToRemove = [NSMutableArray arrayWithArray:slicelayers];
-
+
BOOL isOnEnd = ([slicelayers count] && (sliceCount == 0 || sum <= 0));
if(isOnEnd)
{
for(SliceLayer *layer in _pieView.layer.sublayers){
[self updateLabelForLayer:layer value:0];
[layer createArcAnimationForKey:@"startAngle"
fromValue:[NSNumber numberWithDouble:_startPieAngle]
- toValue:[NSNumber numberWithDouble:_startPieAngle]
+ toValue:[NSNumber numberWithDouble:_startPieAngle]
Delegate:self];
- [layer createArcAnimationForKey:@"endAngle"
+ [layer createArcAnimationForKey:@"endAngle"
fromValue:[NSNumber numberWithDouble:_startPieAngle]
- toValue:[NSNumber numberWithDouble:_startPieAngle]
+ toValue:[NSNumber numberWithDouble:_startPieAngle]
Delegate:self];
}
[CATransaction commit];
return;
}
-
+
for(int index = 0; index < sliceCount; index ++)
{
SliceLayer *layer;
double angle = angles[index];
endToAngle += angle;
double startFromAngle = _startPieAngle + startToAngle;
double endFromAngle = _startPieAngle + endToAngle;
-
+
if( index >= [slicelayers count] )
{
layer = [self createSliceLayer];
@@ -384,7 +386,7 @@ - (void)reloadData
}
else if(diff < 0)
{
- while(diff < 0)
+ while(diff < 0)
{
[onelayer removeFromSuperlayer];
[parentLayer addSublayer:onelayer];
@@ -399,29 +401,34 @@ - (void)reloadData
}
}
}
-
+
layer.value = values[index];
layer.percentage = (sum)?layer.value/sum:0;
UIColor *color = nil;
if([_dataSource respondsToSelector:@selector(pieChart:colorForSliceAtIndex:)])
{
color = [_dataSource pieChart:self colorForSliceAtIndex:index];
}
-
+
if(!color)
{
color = [UIColor colorWithHue:((index/8)%20)/20.0+0.02 saturation:(index%8+3)/10.0 brightness:91/100.0 alpha:1];
}
-
+
[layer setFillColor:color.CGColor];
+ if([_dataSource respondsToSelector:@selector(pieChart:textForSliceAtIndex:)])
+ {
+ layer.text = [_dataSource pieChart:self textForSliceAtIndex:index];
+ }
+
[self updateLabelForLayer:layer value:values[index]];
[layer createArcAnimationForKey:@"startAngle"
fromValue:[NSNumber numberWithDouble:startFromAngle]
- toValue:[NSNumber numberWithDouble:startToAngle+_startPieAngle]
+ toValue:[NSNumber numberWithDouble:startToAngle+_startPieAngle]
Delegate:self];
- [layer createArcAnimationForKey:@"endAngle"
+ [layer createArcAnimationForKey:@"endAngle"
fromValue:[NSNumber numberWithDouble:endFromAngle]
- toValue:[NSNumber numberWithDouble:endToAngle+_startPieAngle]
+ toValue:[NSNumber numberWithDouble:endToAngle+_startPieAngle]
Delegate:self];
startToAngle = endToAngle;
}
@@ -442,25 +449,25 @@ - (void)reloadData
#pragma mark - Animation Delegate + Run Loop Timer
- (void)updateTimerFired:(NSTimer *)timer;
-{
+{
CALayer *parentLayer = [_pieView layer];
NSArray *pieLayers = [parentLayer sublayers];
[pieLayers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
-
+
NSNumber *presentationLayerStartAngle = [[obj presentationLayer] valueForKey:@"startAngle"];
CGFloat interpolatedStartAngle = [presentationLayerStartAngle doubleValue];
-
+
NSNumber *presentationLayerEndAngle = [[obj presentationLayer] valueForKey:@"endAngle"];
CGFloat interpolatedEndAngle = [presentationLayerEndAngle doubleValue];
CGPathRef path = CGPathCreateArc(_pieCenter, _pieRadius, interpolatedStartAngle, interpolatedEndAngle);
[obj setPath:path];
CFRelease(path);
-
+
{
CALayer *labelLayer = [[obj sublayers] objectAtIndex:0];
- CGFloat interpolatedMidAngle = (interpolatedEndAngle + interpolatedStartAngle) / 2;
+ CGFloat interpolatedMidAngle = (interpolatedEndAngle + interpolatedStartAngle) / 2;
[CATransaction setDisableActions:YES];
[labelLayer setPosition:CGPointMake(_pieCenter.x + (_labelRadius * cos(interpolatedMidAngle)), _pieCenter.y + (_labelRadius * sin(interpolatedMidAngle)))];
[CATransaction setDisableActions:NO];
@@ -474,14 +481,14 @@ - (void)animationDidStart:(CAAnimation *)anim
static float timeInterval = 1.0/60.0;
_animationTimer= [NSTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(updateTimerFired:) userInfo:nil repeats:YES];
}
-
+
[_animations addObject:anim];
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)animationCompleted
{
[_animations removeObject:anim];
-
+
if ([_animations count] == 0) {
[_animationTimer invalidate];
_animationTimer = nil;
@@ -493,16 +500,16 @@ - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)animationCompleted
- (NSInteger)getCurrentSelectedOnTouch:(CGPoint)point
{
__block NSUInteger selectedIndex = -1;
-
+
CGAffineTransform transform = CGAffineTransformIdentity;
-
+
CALayer *parentLayer = [_pieView layer];
NSArray *pieLayers = [parentLayer sublayers];
-
+
[pieLayers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
SliceLayer *pieLayer = (SliceLayer *)obj;
CGPathRef path = [pieLayer path];
-
+
if (CGPathContainsPoint(path, &transform, point, 0)) {
[pieLayer setLineWidth:_selectedSliceStroke];
[pieLayer setStrokeColor:[UIColor whiteColor].CGColor];
@@ -542,7 +549,7 @@ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
CALayer *parentLayer = [_pieView layer];
NSArray *pieLayers = [parentLayer sublayers];
-
+
for (SliceLayer *pieLayer in pieLayers) {
[pieLayer setZPosition:kDefaultSliceZOrder];
[pieLayer setLineWidth:0.0];
@@ -553,16 +560,16 @@ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
- (void)notifyDelegateOfSelectionChangeFrom:(NSUInteger)previousSelection to:(NSUInteger)newSelection
{
- if (previousSelection != newSelection)
+ if (previousSelection != newSelection)
{
if (previousSelection != -1 && [_delegate respondsToSelector:@selector(pieChart:willDeselectSliceAtIndex:)])
{
[_delegate pieChart:self willDeselectSliceAtIndex:previousSelection];
}
-
+
_selectedSliceIndex = newSelection;
-
- if (newSelection != -1)
+
+ if (newSelection != -1)
{
if([_delegate respondsToSelector:@selector(pieChart:willSelectSliceAtIndex:)])
[_delegate pieChart:self willSelectSliceAtIndex:newSelection];
@@ -572,7 +579,7 @@ - (void)notifyDelegateOfSelectionChangeFrom:(NSUInteger)previousSelection to:(NS
[_delegate pieChart:self didSelectSliceAtIndex:newSelection];
[self setSliceSelectedAtIndex:newSelection];
}
-
+
if(previousSelection != -1)
{
[self setSliceDeselectedAtIndex:previousSelection];
@@ -584,7 +591,7 @@ - (void)notifyDelegateOfSelectionChangeFrom:(NSUInteger)previousSelection to:(NS
{
SliceLayer *layer = [_pieView.layer.sublayers objectAtIndex:newSelection];
if(_selectedSliceOffsetRadius > 0 && layer){
-
+
if (layer.isSelected) {
if ([_delegate respondsToSelector:@selector(pieChart:willDeselectSliceAtIndex:)])
[_delegate pieChart:self willDeselectSliceAtIndex:newSelection];
@@ -636,9 +643,10 @@ - (void)updateLabelForLayer:(SliceLayer *)pieLayer value:(CGFloat)value
if(_showPercentage)
label = [NSString stringWithFormat:@"%0.0f", pieLayer.percentage*100];
else
- label = [NSString stringWithFormat:@"%0.0f", value];
+ label = (pieLayer.text)?pieLayer.text:[NSString stringWithFormat:@"%0.0f", value];
+
CGSize size = [label sizeWithFont:self.labelFont];
-
+
[CATransaction setDisableActions:YES];
if(M_PI*2*_labelRadius*pieLayer.percentage < MAX(size.width,size.height) || value <= 0)
{

0 comments on commit 9bb14e7

Please sign in to comment.