直接上代码了,这里加上了动画,代码中记录了我是怎么想的,怎么做的,后期会加上点击功能。
#import "GY_PieChart.h"
@interface GY_PieChart ()
@property (nonatomic, assign) CGFloat startAngle;
@property (nonatomic, assign) CGFloat endAngle;
@property (nonatomic, strong) NSMutableArray *valuesAry;
@property (nonatomic, strong) NSMutableArray *colorsAry;
@property (nonatomic, strong) NSMutableArray * arcLayers;
@property (nonatomic, assign) NSInteger currentArcIndex; //用于标记当前播放到那个扇形了
@end
@implementation GY_PieChart
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setBackgroundColor:[UIColor cyanColor]];
_currentArcIndex = 0;
_totalPlayTime = 1;
_valuesAry = [[NSMutableArray alloc] initWithObjects:@"20",@"10",@"30",@"40", nil];
_colorsAry = [[NSMutableArray alloc] initWithObjects:[UIColor blackColor],[UIColor yellowColor],[UIColor brownColor],[UIColor whiteColor], nil];
_arcLayers = [[NSMutableArray alloc] init];
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
#pragma mark - 动画
/**
* 填充动画过程
*
* @return CABasicAnimation
*/
- (CABasicAnimation *)animation {
CABasicAnimation * fillAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
fillAnimation.duration = [self cacaulatecurrentArcDuration];
fillAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
fillAnimation.fillMode = kCAFillModeForwards;
fillAnimation.removedOnCompletion = YES;
fillAnimation.fromValue = @(0.f);
fillAnimation.toValue = @(1.f);
fillAnimation.delegate = self;
return fillAnimation;
}
/**
* 这是要绘制的扇形的路径,路径交给shapeLayer:去绘制扇形
*
* @return UIBezierPath
*/
- (UIBezierPath *)fill {
//需要多少度的圆弧
CGPoint arcCenter = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
UIBezierPath *_bezierpath = [UIBezierPath bezierPathWithArcCenter:arcCenter
radius:self.frame.size.width/4
startAngle:_startAngle
endAngle:_endAngle
clockwise:YES];
_startAngle = _endAngle;
return _bezierpath;
}
//制作扇形,添加动画
- (CAShapeLayer *)shapeLayer:(UIColor *)color {
CAShapeLayer * layer = [CAShapeLayer layer];
layer.fillColor = nil;
layer.strokeColor = color.CGColor;
layer.lineWidth = self.frame.size.width/2;
layer.path = [self fill].CGPath;
CABasicAnimation * animation = [self animation];
[layer addAnimation:animation forKey:nil];
return layer;
}
//移除绘制的扇形
- (void)removeAllSubLayers {
for (CAShapeLayer * layer in _arcLayers) {
[layer removeFromSuperlayer];
}
[_arcLayers removeAllObjects];
}
/**
* 重绘,设置初始值
*/
- (void)strokePath {
[self removeAllSubLayers];
_currentArcIndex = 0;
_startAngle = -M_PI_2;
[self strokeSinglePath];
}
/**
* 重绘单个扇形,这里是准备绘制扇形的参数
*/
- (void)strokeSinglePath {
_endAngle = _startAngle + [self cacaulatecurrentArcPercent]*M_PI*2;
[self startStroke];
}
/**
* 开始干活,绘制扇形添加到页面上,只管干活,判断什么的不用我管,给我什么我绘制什么
*/
- (void)startStroke {
NSLog(@"do:%ld",_currentArcIndex);
CAShapeLayer *layer = [self shapeLayer:[self.colorsAry objectAtIndex:_currentArcIndex]];
[self.layer addSublayer:layer];
[self.arcLayers addObject:layer];
}
/**
* 是否是最后一个扇形,用于在绘制最后一个扇形时,设置endAngle
*/
- (BOOL)isLast {
if (_currentArcIndex==[_valuesAry count]-1) {
return YES;
}
return NO;
}
/**
* 计算每个扇形的数值占总数的比例
*/
- (CGFloat)cacaulatecurrentArcPercent {
CGFloat valueF = [[_valuesAry objectAtIndex:_currentArcIndex] floatValue];
CGFloat rate = valueF/100;
return rate;
}
/**
* 计算每个扇形绘制动画需要的时间,这里以画一圈圆需要2秒为默认值,每个扇形根据它自己数值的大小去分这2秒
*/
- (CGFloat)cacaulatecurrentArcDuration {
NSLog(@"animition time: %f",[self cacaulatecurrentArcPercent]*_totalPlayTime);
return [self cacaulatecurrentArcPercent]*_totalPlayTime;
}
/**
* 这是我思想的核心,我的想法是当一个扇形的动画展示完毕,再去绘制下一个,知道结束
*/
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
if (flag) {
_currentArcIndex++;
if (_currentArcIndex < [_valuesAry count]) {
if ([self isLast]) {
_endAngle = M_PI*0.75*2;
}
[self strokeSinglePath];
}
}
}
@end