利用了几个中午的时间,终于有点效果了,得贴出来纪念下~~
#import <QuartzCore/QuartzCore.h> #define SAngle @"StartAngle" #define EAngle @"EndAngle" typedef struct { CGFloat BeginAngle; CGFloat StartAngle; CGFloat EndAngle; }stPieAngleSet; @interface PieLayer : CAShapeLayer @property(nonatomic,assign) stPieAngleSet Angles; @property(nonatomic,weak) id CADelegate; @property(nonatomic,assign) CFTimeInterval CycleTime; -(void)CreateAnimations; @end
#import "PieLayer.h" @implementation PieLayer -(void)CreateAnimations { /*创建两组动画,这里的动画没起作用,只提供了2个值,然后进行setPath*/ CABasicAnimation* base1 = [CABasicAnimation animation]; NSNumber* S = [NSNumber numberWithFloat:self.Angles.BeginAngle]; NSNumber* E = [NSNumber numberWithFloat:self.Angles.BeginAngle + self.Angles.StartAngle]; NSNumber* CS = [[self presentationLayer] valueForKey:SAngle]; if (!CS) CS = S; [base1 setFromValue:CS]; [base1 setToValue:E]; [base1 setDuration:self.CycleTime]; [base1 setDelegate:self.CADelegate]; [base1 setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]]; [self addAnimation:base1 forKey:SAngle]; [self setValue:E forKey:SAngle]; CABasicAnimation* base2 = [CABasicAnimation animation]; S = [NSNumber numberWithFloat:self.Angles.BeginAngle]; E = [NSNumber numberWithFloat:self.Angles.BeginAngle + self.Angles.EndAngle]; CS = [[self presentationLayer] valueForKey:EAngle]; if (!CS) CS = S; [base2 setFromValue:CS]; [base2 setToValue:E]; [base2 setDuration:self.CycleTime]; [base2 setDelegate:self.CADelegate]; [base2 setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]]; [self setValue:E forKey:EAngle]; [self addAnimation:base2 forKey:EAngle]; } @end
#import <UIKit/UIKit.h> @protocol PieViewDelegate <NSObject> @required -(void)show; @end @protocol PieViewSourceDelegate <NSObject> @required -(NSInteger)NumberOfParts; -(NSInteger)PartValue:(NSInteger) Index; -(UIColor*)PartColor:(NSInteger) Index; @end @interface PieView : UIView @property (nonatomic,assign) CGFloat Radius; @property (nonatomic,assign) CGFloat BeginAngle; @property (nonatomic,assign) CFTimeInterval CycleTime; @property (nonatomic,weak) id<PieViewSourceDelegate> SourceDelegate; -(instancetype)initWithFrame:(CGRect)frame; -(void)Refresh; -(void)ReCovery; @end
#import "PieView.h" #import "PieLayer.h" @implementation PieView { @private NSTimer* _timer; NSMutableArray *_animations; } -(instancetype)initWithFrame:(CGRect)frame { if ([super initWithFrame:frame]) { _animations = [NSMutableArray arrayWithCapacity:5]; _timer = nil; self.Radius = MIN(frame.size.width / 2, frame.size.height / 2); self.BeginAngle = 0; self.CycleTime = 1; } return self; } -(void)ReCovery { NSInteger iCounts = self.layer.sublayers.count; if (iCounts == 0) return; __block stPieAngleSet sSet; sSet.BeginAngle = self.BeginAngle; sSet.StartAngle = 0.0; sSet.EndAngle = 0.0; [self.layer.sublayers enumerateObjectsUsingBlock:^(PieLayer* obj, NSUInteger idx, BOOL *stop) { [CATransaction begin]; obj.Angles = sSet; [obj CreateAnimations]; [CATransaction commit]; }]; } -(void)Refresh { if (!self.SourceDelegate) return; /*删除所有的layer,直接用self.layer.sublayers还有错误,和线程访问相关*/ NSArray* arrRecovery = [NSArray arrayWithArray:self.layer.sublayers]; if (arrRecovery.count > 0) { [arrRecovery enumerateObjectsUsingBlock:^(CAShapeLayer* obj, NSUInteger idx, BOOL *stop) { [obj removeFromSuperlayer]; }]; } /*获取数目*/ NSInteger iPartCount = [self.SourceDelegate NumberOfParts]; NSInteger iValues[iPartCount]; NSInteger iSum = 0; /*获取值*/ for (NSInteger i = 0; i < iPartCount; i++) { iValues[i] = [self.SourceDelegate PartValue:i]; iSum += iValues[i]; } if (iSum == 0) return; /*计算角度,弧度表示*/ CGFloat fAngles[iPartCount]; for (NSInteger i = 0; i < iPartCount; i++) { fAngles[i] = M_PI * 2 * iValues[i] / iSum; } stPieAngleSet sSets[iPartCount]; CGFloat StartAngle = 0.0; for (NSInteger i = 0; i < iPartCount; i++) { sSets[i].BeginAngle = self.BeginAngle; sSets[i].StartAngle = StartAngle; sSets[i].EndAngle = StartAngle + fAngles[i]; StartAngle += fAngles[i]; } /*创建layer*/ for (NSInteger i = 0; i < iPartCount; i++) { [CATransaction begin];/*认为可以不用加,因为创建了还看不到呢*/ PieLayer* Layer = [PieLayer layer]; [self.layer addSublayer:Layer]; Layer.Angles = sSets[i]; Layer.CADelegate = self; Layer.fillColor = [[self.SourceDelegate PartColor:i] CGColor]; Layer.CycleTime = self.CycleTime; [Layer CreateAnimations]; [CATransaction commit]; } } -(void)TimerFired:(NSTimer*) timer { [self.layer.sublayers enumerateObjectsUsingBlock:^(CAShapeLayer* obj, NSUInteger idx, BOOL *stop) { NSNumber* StartAngle = [[obj presentationLayer] valueForKey:SAngle]; NSNumber* EndAngle = [[obj presentationLayer] valueForKey:EAngle]; CGFloat S = [StartAngle floatValue]; CGFloat E = [EndAngle floatValue]; CGPoint _Center = self.layer.position; CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, NULL, _Center.x, _Center.y); CGPathAddArc(path, NULL, _Center.x, _Center.y, self.Radius, S, E, 0); CGPathCloseSubpath(path); [obj setPath:path]; }]; } -(void)animationDidStart:(CAAnimation *)anim { if (!_timer) { static float timeInterval = 1.0/60.0; _timer = [NSTimer timerWithTimeInterval:timeInterval target:self selector:@selector(TimerFired:) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; } [_animations addObject:anim]; } -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { [_animations removeObject:anim]; if (_animations.count == 0) { [_timer invalidate]; _timer = nil; } } @end
调用部分
#import "ViewController.h" @interface ViewController () @property (nonatomic, strong) PieView* LeftPie; @end @implementation ViewController { Boolean _IsShowState; } - (void)viewDidLoad { [super viewDidLoad]; self.LeftPie = [[PieView alloc] initWithFrame:CGRectMake(20, 20, 100, 100)]; _IsShowState = NO; [self.view addSubview:self.LeftPie]; self.LeftPie.CycleTime = 2; [self.LeftPie setSourceDelegate:self]; [self.LeftPie setBeginAngle:M_PI / 2]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if (_IsShowState) { [self.LeftPie ReCovery]; } else { [self.LeftPie Refresh]; }; _IsShowState = !_IsShowState; } -(NSInteger)NumberOfParts { return 5; } -(NSInteger)PartValue:(NSInteger)Index { switch (Index) { case 0: return 20; case 1: return 10; case 2: return 30; case 3: return 40; case 4: return 25; } return 20; } -(UIColor*)PartColor:(NSInteger)Index { switch (Index) { case 0: return [UIColor grayColor]; case 1: return [UIColor redColor]; case 2: return [UIColor darkGrayColor]; case 3: return [UIColor blueColor]; case 4: return [UIColor brownColor]; } return [UIColor clearColor]; } @end
总结下吧:
1,虽然说属于动画部分,但没有用到动画效果,只是利用动画的中间值进行了layer的填充(自己的理解)
2,这个还不完善,没有值和百分比的显示,也不能点击,更不是三维的,后期继续添加~~
核心思想出自一个ipad程序,里面写的挺复杂的,我做了提炼。工程的名字叫xyPieChart,链接忘了,作者看了请勿喷~~