123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- //
- // DACircularProgressView.m
- // DACircularProgress
- //
- // Created by Daniel Amitay on 2/6/12.
- // Copyright (c) 2012 Daniel Amitay. All rights reserved.
- //
- #import "DACircularProgressView.h"
- #import <QuartzCore/QuartzCore.h>
- @interface DACircularProgressLayer : CALayer
- @property(nonatomic, strong) UIColor *trackTintColor;
- @property(nonatomic, strong) UIColor *progressTintColor;
- @property(nonatomic) NSInteger roundedCorners;
- @property(nonatomic) CGFloat thicknessRatio;
- @property(nonatomic) CGFloat progress;
- @end
- @implementation DACircularProgressLayer
- @dynamic trackTintColor;
- @dynamic progressTintColor;
- @dynamic roundedCorners;
- @dynamic thicknessRatio;
- @dynamic progress;
- + (BOOL)needsDisplayForKey:(NSString *)key
- {
- return [key isEqualToString:@"progress"] ? YES : [super needsDisplayForKey:key];
- }
- - (void)drawInContext:(CGContextRef)context
- {
- CGRect rect = self.bounds;
- CGPoint centerPoint = CGPointMake(rect.size.height / 2.0f, rect.size.width / 2.0f);
- CGFloat radius = MIN(rect.size.height, rect.size.width) / 2.0f;
-
- CGFloat progress = MIN(self.progress, 1.0f - FLT_EPSILON);
- CGFloat radians = (progress * 2.0f * M_PI) - M_PI_2;
-
- CGContextSetFillColorWithColor(context, self.trackTintColor.CGColor);
- CGMutablePathRef trackPath = CGPathCreateMutable();
- CGPathMoveToPoint(trackPath, NULL, centerPoint.x, centerPoint.y);
- CGPathAddArc(trackPath, NULL, centerPoint.x, centerPoint.y, radius, 3.0f * M_PI_2, -M_PI_2, NO);
- CGPathCloseSubpath(trackPath);
- CGContextAddPath(context, trackPath);
- CGContextFillPath(context);
- CGPathRelease(trackPath);
-
- if (progress > 0.0f)
- {
- CGContextSetFillColorWithColor(context, self.progressTintColor.CGColor);
- CGMutablePathRef progressPath = CGPathCreateMutable();
- CGPathMoveToPoint(progressPath, NULL, centerPoint.x, centerPoint.y);
- CGPathAddArc(progressPath, NULL, centerPoint.x, centerPoint.y, radius, 3.0f * M_PI_2, radians, NO);
- CGPathCloseSubpath(progressPath);
- CGContextAddPath(context, progressPath);
- CGContextFillPath(context);
- CGPathRelease(progressPath);
- }
-
- if (progress > 0.0f && self.roundedCorners)
- {
- CGFloat pathWidth = radius * self.thicknessRatio;
- CGFloat xOffset = radius * (1.0f + ((1.0f - (self.thicknessRatio / 2.0f)) * cosf(radians)));
- CGFloat yOffset = radius * (1.0f + ((1.0f - (self.thicknessRatio / 2.0f)) * sinf(radians)));
- CGPoint endPoint = CGPointMake(xOffset, yOffset);
-
- CGContextAddEllipseInRect(context, CGRectMake(centerPoint.x - pathWidth / 2.0f, 0.0f, pathWidth, pathWidth));
- CGContextFillPath(context);
-
- CGContextAddEllipseInRect(context, CGRectMake(endPoint.x - pathWidth / 2.0f, endPoint.y - pathWidth / 2.0f, pathWidth, pathWidth));
- CGContextFillPath(context);
- }
-
- CGContextSetBlendMode(context, kCGBlendModeClear);
- CGFloat innerRadius = radius * (1.0f - self.thicknessRatio);
- CGPoint newCenterPoint = CGPointMake(centerPoint.x - innerRadius, centerPoint.y - innerRadius);
- CGContextAddEllipseInRect(context, CGRectMake(newCenterPoint.x, newCenterPoint.y, innerRadius * 2.0f, innerRadius * 2.0f));
- CGContextFillPath(context);
- }
- @end
- @interface DACircularProgressView ()
- @end
- @implementation DACircularProgressView
- + (void) initialize
- {
- if (self != [DACircularProgressView class])
- return;
-
- id appearance = [self appearance];
- [appearance setTrackTintColor:[[UIColor whiteColor] colorWithAlphaComponent:0.3f]];
- [appearance setProgressTintColor:[UIColor whiteColor]];
- [appearance setBackgroundColor:[UIColor clearColor]];
- [appearance setThicknessRatio:0.3f];
- [appearance setRoundedCorners:NO];
-
- [appearance setIndeterminateDuration:2.0f];
- [appearance setIndeterminate:NO];
- }
- + (Class)layerClass
- {
- return [DACircularProgressLayer class];
- }
- - (DACircularProgressLayer *)circularProgressLayer
- {
- return (DACircularProgressLayer *)self.layer;
- }
- - (id)init
- {
- return [super initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 40.0f)];
- }
- - (void)didMoveToWindow
- {
- CGFloat windowContentsScale = self.window.screen.scale;
- self.circularProgressLayer.contentsScale = windowContentsScale;
- }
- #pragma mark - Progress
- - (CGFloat)progress
- {
- return self.circularProgressLayer.progress;
- }
- - (void)setProgress:(CGFloat)progress
- {
- [self setProgress:progress animated:NO];
- }
- - (void)setProgress:(CGFloat)progress animated:(BOOL)animated
- {
- [self.layer removeAnimationForKey:@"indeterminateAnimation"];
- [self.circularProgressLayer removeAnimationForKey:@"progress"];
-
- CGFloat pinnedProgress = MIN(MAX(progress, 0.0f), 1.0f);
- if (animated)
- {
- CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"progress"];
- animation.duration = fabs(self.progress - pinnedProgress); // Same duration as UIProgressView animation
- animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
- animation.fromValue = [NSNumber numberWithFloat:self.progress];
- animation.toValue = [NSNumber numberWithFloat:pinnedProgress];
- [self.circularProgressLayer addAnimation:animation forKey:@"progress"];
- }
- else
- {
- [self.circularProgressLayer setNeedsDisplay];
- }
- self.circularProgressLayer.progress = pinnedProgress;
- }
- #pragma mark - UIAppearance methods
- - (UIColor *)trackTintColor
- {
- return self.circularProgressLayer.trackTintColor;
- }
- - (void)setTrackTintColor:(UIColor *)trackTintColor
- {
- self.circularProgressLayer.trackTintColor = trackTintColor;
- [self.circularProgressLayer setNeedsDisplay];
- }
- - (UIColor *)progressTintColor
- {
- return self.circularProgressLayer.progressTintColor;
- }
- - (void)setProgressTintColor:(UIColor *)progressTintColor
- {
- self.circularProgressLayer.progressTintColor = progressTintColor;
- [self.circularProgressLayer setNeedsDisplay];
- }
- - (NSInteger)roundedCorners
- {
- return self.roundedCorners;
- }
- - (void)setRoundedCorners:(NSInteger)roundedCorners
- {
- self.circularProgressLayer.roundedCorners = roundedCorners;
- [self.circularProgressLayer setNeedsDisplay];
- }
- - (CGFloat)thicknessRatio
- {
- return self.circularProgressLayer.thicknessRatio;
- }
- - (void)setThicknessRatio:(CGFloat)thicknessRatio
- {
- self.circularProgressLayer.thicknessRatio = MIN(MAX(thicknessRatio, 0.f), 1.f);
- [self.circularProgressLayer setNeedsDisplay];
- }
- - (NSInteger)indeterminate
- {
- CAAnimation *spinAnimation = [self.layer animationForKey:@"indeterminateAnimation"];
- return (spinAnimation == nil ? 0 : 1);
- }
- - (void)setIndeterminate:(NSInteger)indeterminate
- {
- if (indeterminate && !self.indeterminate)
- {
- CABasicAnimation *spinAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
- spinAnimation.byValue = [NSNumber numberWithFloat:indeterminate > 0 ? 2.0f*M_PI : -2.0f*M_PI];
- spinAnimation.duration = self.indeterminateDuration;
- spinAnimation.repeatCount = HUGE_VALF;
- [self.layer addAnimation:spinAnimation forKey:@"indeterminateAnimation"];
- }
- else
- {
- [self.layer removeAnimationForKey:@"indeterminateAnimation"];
- }
- }
- @end
|