123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- //
- // 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, strong) UIColor *innerTintColor;
- @property(nonatomic) NSInteger roundedCorners;
- @property(nonatomic) CGFloat thicknessRatio;
- @property(nonatomic) CGFloat progress;
- @property(nonatomic) NSInteger clockwiseProgress;
- @end
- @implementation DACircularProgressLayer
- @dynamic trackTintColor;
- @dynamic progressTintColor;
- @dynamic innerTintColor;
- @dynamic roundedCorners;
- @dynamic thicknessRatio;
- @dynamic progress;
- @dynamic clockwiseProgress;
- + (BOOL)needsDisplayForKey:(NSString *)key
- {
- if ([key isEqualToString:@"progress"]) {
- return YES;
- } else {
- return [super needsDisplayForKey:key];
- }
- }
- - (void)drawInContext:(CGContextRef)context
- {
- CGRect rect = self.bounds;
- CGPoint centerPoint = CGPointMake(rect.size.width / 2.0f, rect.size.height / 2.0f);
- CGFloat radius = MIN(rect.size.height, rect.size.width) / 2.0f;
-
- BOOL clockwise = (self.clockwiseProgress != 0);
-
- CGFloat progress = MIN(self.progress, 1.0f - FLT_EPSILON);
- CGFloat radians = 0;
- if (clockwise) {
- radians = (float)((progress * 2.0f * M_PI) - M_PI_2);
- } else {
- radians = (float)(3 * M_PI_2 - (progress * 2.0f * M_PI));
- }
-
- CGContextSetFillColorWithColor(context, self.trackTintColor.CGColor);
- CGMutablePathRef trackPath = CGPathCreateMutable();
- CGPathMoveToPoint(trackPath, NULL, centerPoint.x, centerPoint.y);
- CGPathAddArc(trackPath, NULL, centerPoint.x, centerPoint.y, radius, (float)(2.0f * M_PI), 0.0f, TRUE);
- 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, (float)(3.0f * M_PI_2), radians, !clockwise);
- 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);
-
- CGRect startEllipseRect = (CGRect) {
- .origin.x = centerPoint.x - pathWidth / 2.0f,
- .origin.y = 0.0f,
- .size.width = pathWidth,
- .size.height = pathWidth
- };
- CGContextAddEllipseInRect(context, startEllipseRect);
- CGContextFillPath(context);
-
- CGRect endEllipseRect = (CGRect) {
- .origin.x = endPoint.x - pathWidth / 2.0f,
- .origin.y = endPoint.y - pathWidth / 2.0f,
- .size.width = pathWidth,
- .size.height = pathWidth
- };
- CGContextAddEllipseInRect(context, endEllipseRect);
- CGContextFillPath(context);
- }
- CGContextSetBlendMode(context, kCGBlendModeClear);
- CGFloat innerRadius = radius * (1.0f - self.thicknessRatio);
- CGRect clearRect = (CGRect) {
- .origin.x = centerPoint.x - innerRadius,
- .origin.y = centerPoint.y - innerRadius,
- .size.width = innerRadius * 2.0f,
- .size.height = innerRadius * 2.0f
- };
- CGContextAddEllipseInRect(context, clearRect);
- CGContextFillPath(context);
- if (self.innerTintColor) {
- CGContextSetBlendMode(context, kCGBlendModeNormal);
- CGContextSetFillColorWithColor(context, [self.innerTintColor CGColor]);
- CGContextAddEllipseInRect(context, clearRect);
- CGContextFillPath(context);
- }
- }
- @end
- @interface DACircularProgressView ()
- @end
- @implementation DACircularProgressView
- + (void) initialize
- {
- if (self == [DACircularProgressView class]) {
- DACircularProgressView *circularProgressViewAppearance = [DACircularProgressView appearance];
- [circularProgressViewAppearance setTrackTintColor:[[UIColor whiteColor] colorWithAlphaComponent:0.3f]];
- [circularProgressViewAppearance setProgressTintColor:[UIColor whiteColor]];
- [circularProgressViewAppearance setInnerTintColor:nil];
- [circularProgressViewAppearance setBackgroundColor:[UIColor clearColor]];
- [circularProgressViewAppearance setThicknessRatio:0.3f];
- [circularProgressViewAppearance setRoundedCorners:NO];
- [circularProgressViewAppearance setClockwiseProgress:YES];
-
- [circularProgressViewAppearance setIndeterminateDuration:2.0f];
- [circularProgressViewAppearance 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
- {
- [super didMoveToWindow];
- CGFloat windowContentsScale = self.window.screen.scale;
- self.circularProgressLayer.contentsScale = windowContentsScale;
- [self.circularProgressLayer setNeedsDisplay];
- }
- #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 setProgress:progress animated:animated initialDelay:0.0];
- }
- - (void)setProgress:(CGFloat)progress
- animated:(BOOL)animated
- initialDelay:(CFTimeInterval)initialDelay
- {
- CGFloat pinnedProgress = MIN(MAX(progress, 0.0f), 1.0f);
- [self setProgress:progress
- animated:animated
- initialDelay:initialDelay
- withDuration:fabs(self.progress - pinnedProgress)];
- }
- - (void)setProgress:(CGFloat)progress
- animated:(BOOL)animated
- initialDelay:(CFTimeInterval)initialDelay
- withDuration:(CFTimeInterval)duration
- {
- [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 = duration;
- animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
- animation.fillMode = kCAFillModeForwards;
- animation.fromValue = [NSNumber numberWithFloat:self.progress];
- animation.toValue = [NSNumber numberWithFloat:pinnedProgress];
- animation.beginTime = CACurrentMediaTime() + initialDelay;
- animation.delegate = self;
- [self.circularProgressLayer addAnimation:animation forKey:@"progress"];
- } else {
- [self.circularProgressLayer setNeedsDisplay];
- self.circularProgressLayer.progress = pinnedProgress;
- }
- }
- - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag
- {
- NSNumber *pinnedProgressNumber = [animation valueForKey:@"toValue"];
- self.circularProgressLayer.progress = [pinnedProgressNumber floatValue];
- }
- #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];
- }
- - (UIColor *)innerTintColor
- {
- return self.circularProgressLayer.innerTintColor;
- }
- - (void)setInnerTintColor:(UIColor *)innerTintColor
- {
- self.circularProgressLayer.innerTintColor = innerTintColor;
- [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) {
- if (!self.indeterminate) {
- CABasicAnimation *spinAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
- spinAnimation.byValue = [NSNumber numberWithDouble: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"];
- }
- }
- - (NSInteger)clockwiseProgress
- {
- return self.circularProgressLayer.clockwiseProgress;
- }
- - (void)setClockwiseProgress:(NSInteger)clockwiseProgres
- {
- self.circularProgressLayer.clockwiseProgress = clockwiseProgres;
- [self.circularProgressLayer setNeedsDisplay];
- }
- @end
|