Skip to content

Instantly share code, notes, and snippets.

@cyhsutw
Last active November 29, 2015 00:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cyhsutw/515a96834023802df030 to your computer and use it in GitHub Desktop.
Save cyhsutw/515a96834023802df030 to your computer and use it in GitHub Desktop.
Gradient Ring
//
// Spinner.h
//
// Created by Cheng-Yu Hsu on 11/28/15.
//
#import <UIKit/UIKit.h>
@interface Spinner : UIView
- (instancetype)initWithCenter:(CGPoint)center
radius:(CGFloat)radius
strokeWidth:(CGFloat)strokeWidth
startColor:(UIColor *)startColor
endColor:(UIColor *)endColor
mirrored:(BOOL)mirrored;
- (void)startAnimating;
@end
//
// Spinner.m
//
// Created by Cheng-Yu Hsu on 11/28/15.
//
#import "Spinner.h"
@interface Spinner ()
@property (nonatomic, assign) CGFloat strokeWidth;
@property (nonatomic, assign) CGFloat radius;
@property (nonatomic, strong) UIColor *startColor;
@property (nonatomic, strong) UIColor *endColor;
@property (nonatomic, assign) BOOL mirrored;
@end
@implementation Spinner
- (instancetype)initWithCenter:(CGPoint)center
radius:(CGFloat)radius
strokeWidth:(CGFloat)strokeWidth
startColor:(UIColor *)startColor
endColor:(UIColor *)endColor
mirrored:(BOOL)mirrored
{
self = [super initWithFrame:CGRectMake(center.x - radius, center.y - radius, radius * 2, radius * 2)];
if(self){
[self setBackgroundColor:[UIColor clearColor]];
_radius = radius;
_strokeWidth = strokeWidth;
_startColor = startColor;
_endColor = endColor;
_mirrored = mirrored;
[self.layer setCornerRadius:radius];
[self setClipsToBounds:YES];
}
return self;
}
- (void)startAnimating
{
if ([self.layer animationForKey:@"SpinAnimation"] == nil) {
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
animation.fromValue = [NSNumber numberWithFloat:_mirrored ? 2 * M_PI : 0.0];
animation.toValue = [NSNumber numberWithFloat: _mirrored ? 0.0 : 2 * M_PI];
animation.duration = 2.0f;
animation.repeatCount = INFINITY;
animation.removedOnCompletion = NO;
[self.layer addAnimation:animation forKey:@"SpinAnimation"];
}
}
// override drawRect
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(ctx, kCGBlendModeNormal);
CGFloat gradientColors[4 * 2];
extractColorComponents(_startColor, gradientColors, 0);
extractColorComponents(_endColor, gradientColors, 4);
CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, gradientColors, NULL, 2);
CGColorSpaceRelease(baseSpace);
baseSpace = nil;
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint, 0);
CGGradientRelease(gradient);
gradient = nil;
// fill 'clear' color
CGContextSetFillColorWithColor(ctx, [UIColor clearColor].CGColor);
CGContextSetStrokeColorWithColor(ctx, [UIColor clearColor].CGColor);
CGContextSetBlendMode(ctx, kCGBlendModeClear);
CGFloat innerCircleRadius = _radius - _strokeWidth;
// fill an 'empty' hole inside the gradient part
CGContextFillEllipseInRect(ctx, (CGRect){
{_strokeWidth, _strokeWidth},
{innerCircleRadius * 2, innerCircleRadius * 2}
});
// fill an 'empty' segment of arc
CGFloat startAngle = _mirrored ? (0.9 * M_PI) : (-0.1 * M_PI);
CGFloat endAngle = startAngle + 0.2 * M_PI;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:(CGPoint){_radius, _radius}
radius:_radius - _strokeWidth * 0.5
startAngle:startAngle
endAngle:endAngle
clockwise:YES];
path.lineWidth = _strokeWidth;
CGContextAddPath(ctx, path.CGPath);
CGContextSetLineWidth(ctx, _strokeWidth + 2);
CGContextStrokePath(ctx);
CGContextSetBlendMode(ctx, kCGBlendModeNormal);
}
void extractColorComponents(UIColor *color, CGFloat components[], int startIndex)
{
CGColorRef colorRef = color.CGColor;
int numberOfComponents = CGColorGetNumberOfComponents(colorRef);
if(numberOfComponents == 4){
// RGB & alpha
const CGFloat *comps = CGColorGetComponents(colorRef);
components[startIndex + 0] = comps[0];
components[startIndex + 1] = comps[1];
components[startIndex + 2] = comps[2];
components[startIndex + 3] = comps[3];
}else if (numberOfComponents == 2){
// gray scale & alpha
const CGFloat *comps = CGColorGetComponents(colorRef);
components[startIndex + 0] = comps[0];
components[startIndex + 1] = comps[0];
components[startIndex + 2] = comps[0];
components[startIndex + 3] = comps[1];
}
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment