, .
, , -. , , .
, , , CircleGradientLayer. , progress CircleProgressView.
, : fooobar.com/questions/1548447/..., ;)
.h , , .m :
CircleProgressView
@implementation CircleProgressView{
CircleGradientLayer *_gradientLayer;
CircleOutlineLayer *_outlineLayer;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
int numSegments = 7;
CGFloat circleRadius = 130;
CGFloat circleWidth = 30;
NSDictionary *circleData = @{
@"numberOfSegments":@(numSegments),
@"circleRadius":@(circleRadius),
@"circleWidth":@(circleWidth)
};
_gradientLayer = [CircleGradientLayer layer];
_gradientLayer.contentsScale = 2;
_gradientLayer.frame = self.bounds;
[_gradientLayer setCircleData:circleData];
[_gradientLayer setNeedsDisplay];
[self.layer addSublayer:_gradientLayer];
_outlineLayer = [CircleOutlineLayer layer];
_outlineLayer.frame = self.bounds;
_outlineLayer.contentsScale = 2;
[_outlineLayer setCircleData:circleData];
[_outlineLayer setNeedsDisplay];
[self.layer addSublayer:_outlineLayer];
self.progress = 1;
}
return self;
}
- (void)setProgress:(CGFloat)progress{
_progress = MAX(0, MIN(1, progress));
_gradientLayer.progress = progress;
}
@end
CircleGradientLayer
@implementation CircleGradientLayer{
int _numSegments;
CGFloat _circleRadius;
CGFloat _circleWidth;
CAShapeLayer *_maskLayer;
}
+(id)layer{
CircleGradientLayer *layer = [[CircleGradientLayer alloc] init];
return layer;
}
-(void)setCircleData:(NSDictionary*)data{
_numSegments = [data[@"numberOfSegments"] intValue];
_circleRadius = [data[@"circleRadius"] doubleValue];
_circleWidth = [data[@"circleWidth"] doubleValue];
[self createMask];
}
- (void)createMask{
_maskLayer = [CAShapeLayer layer];
_maskLayer.frame = self.bounds;
CGFloat angleStep = 2*M_PI/(_numSegments+1);
CGFloat startAngle = angleStep/2 + M_PI_2;
CGFloat endAngle = startAngle+_numSegments*angleStep+0.005;
_maskLayer.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)) radius:_circleRadius startAngle:startAngle endAngle:endAngle clockwise:YES].CGPath;
_maskLayer.fillColor = [UIColor clearColor].CGColor;
_maskLayer.strokeColor = [UIColor redColor].CGColor;
_maskLayer.lineWidth = 2*_circleWidth+2;
self.mask = _maskLayer;
}
- (void)setProgress:(CGFloat)progress{
_progress = MAX(0, MIN(1, progress));
_maskLayer.strokeEnd = _progress;
}
-(void)drawInContext:(CGContextRef)ctx{
UIGraphicsPushContext(ctx);
UIColor *startColor = [UIColor colorWithRed:1 green:0 blue:21/255. alpha:1];
UIColor *endColor = [UIColor colorWithRed:0 green:180/255. blue:35/255. alpha:1];
CGPoint centerPoint = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
CGFloat angleStep = 2*M_PI/(_numSegments+1);
CGFloat startAngle = angleStep/2 + M_PI_2;
CGFloat startHue,startSat,startBrightness,startAlpha;
CGFloat endHue,endSat,endBrightness,endAlpha;
[startColor getHue:&startHue saturation:&startSat brightness:&startBrightness alpha:&startAlpha];
[endColor getHue:&endHue saturation:&endSat brightness:&endBrightness alpha:&endAlpha];
if(endHue<startHue)
endHue+=1;
for(int i=0;i<_numSegments;i++){
CGFloat hue = startHue+((endHue-startHue)*i)/_numSegments;
if(hue>1)
hue-=1;
CGFloat brightness = startBrightness+((endBrightness-startBrightness)*i)/(_numSegments);
if(_numSegments==7){
brightness = i>3?startBrightness+((endBrightness-startBrightness)*(i-4))/(_numSegments-4):startBrightness;
}
UIColor *fromColor = [UIColor colorWithHue:hue saturation:startSat+((endSat-startSat)*i)/_numSegments brightness:brightness alpha:startAlpha+((endAlpha-startAlpha)*i)/_numSegments];
hue = startHue+((endHue-startHue)*(i+1))/_numSegments;
if(hue>1)
hue-=1;
brightness = startBrightness+((endBrightness-startBrightness)*i)/(_numSegments);
if(_numSegments==7){
brightness = i>3?startBrightness+((endBrightness-startBrightness)*(i-3))/(_numSegments-4):startBrightness;
}
UIColor *toColor = [UIColor colorWithHue:hue saturation:startSat+((endSat-startSat)*(i+1))/_numSegments brightness:brightness alpha:startAlpha+((endAlpha-startAlpha)*(i+1))/_numSegments];
[self drawSegmentAtCenter:centerPoint from:startAngle to:startAngle+angleStep radius:_circleRadius width:_circleWidth startColor:fromColor endColor:toColor];
startAngle+=angleStep;
}
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
UIBezierPath* innerPath = [UIBezierPath bezierPath];
[innerPath moveToPoint:centerPoint];
[innerPath addArcWithCenter:centerPoint radius:_circleRadius-_circleWidth-0.5 startAngle:0 endAngle:2*M_PI clockwise:YES];
[innerPath fill];
}
- (void)drawSegmentAtCenter:(CGPoint)center from:(CGFloat)startAngle to:(CGFloat)endAngle radius:(CGFloat)radius width:(CGFloat)width startColor:(UIColor *)startColor endColor:(UIColor*)endColor{
CGContextSaveGState(UIGraphicsGetCurrentContext());
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:center];
[path addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
[path addClip];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = { 0.0, 1.0 };
NSArray *colors = @[(__bridge id) startColor.CGColor, (__bridge id) endColor.CGColor];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
CGPoint startPoint = CGPointMake(center.x-sinf(startAngle-M_PI_2)*radius, center.y+cosf(startAngle-M_PI_2)*radius);
CGPoint endPoint = CGPointMake(center.x-sinf(endAngle-M_PI_2)*radius, center.y+cosf(endAngle-M_PI_2)*radius);
CGContextDrawLinearGradient(UIGraphicsGetCurrentContext(), gradient, startPoint, endPoint, kCGGradientDrawsAfterEndLocation|kCGGradientDrawsBeforeStartLocation);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
CGContextRestoreGState(UIGraphicsGetCurrentContext());
}
@end
CircleOutlineLayer
@implementation CircleOutlineLayer{
int _numSegments;
CGFloat _circleradius;
CGFloat _circlewidth;
}
+(id)layer{
CircleOutlineLayer *layer = [[CircleOutlineLayer alloc] init];
return layer;
}
-(void)setCircleData:(NSDictionary*)data{
_numSegments = [data[@"numberOfSegments"] intValue];
_circleradius = [data[@"circleRadius"] doubleValue];
_circlewidth = [data[@"circleWidth"] doubleValue];
}
-(void)drawInContext:(CGContextRef)ctx{
UIGraphicsPushContext(ctx);
[[UIColor colorWithWhite:130/255. alpha:1] setStroke];
CGPoint centerPoint = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
CGFloat angleStep = 2*M_PI/(_numSegments+1);
CGFloat startAngle = angleStep/2 + M_PI_2;
for(int i=0;i<_numSegments;i++){
[self drawSegmentAtCenter:centerPoint from:startAngle to:startAngle+angleStep radius:_circleradius width:_circlewidth doFill:NO];
startAngle+=angleStep;
}
UIBezierPath *innerPath = [UIBezierPath bezierPath];
[innerPath moveToPoint:centerPoint];
[innerPath addArcWithCenter:centerPoint radius:_circleradius-_circlewidth startAngle:0 endAngle:2*M_PI clockwise:YES];
[innerPath stroke];
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
innerPath = [UIBezierPath bezierPath];
[innerPath moveToPoint:centerPoint];
[innerPath addArcWithCenter:centerPoint radius:_circleradius-_circlewidth-0.5 startAngle:0 endAngle:2*M_PI clockwise:YES];
[innerPath fill];
[self drawSegmentAtCenter:centerPoint from:-angleStep/2 + M_PI_2 to:angleStep/2+M_PI_2 radius:_circleradius width:_circlewidth doFill:YES];
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeNormal);
CGPoint startPoint = CGPointMake(centerPoint.x+sinf(-angleStep/2)*(_circleradius), centerPoint.y+cosf(-angleStep/2)*(_circleradius));
CGPoint endPoint = CGPointMake(centerPoint.x+sinf(-angleStep/2)*(_circleradius-_circlewidth), centerPoint.y+cosf(-angleStep/2)*(_circleradius-_circlewidth));
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 1);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), startPoint.x, startPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), endPoint.x,endPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
startPoint = CGPointMake(centerPoint.x+sinf(angleStep/2)*(_circleradius), centerPoint.y+cosf(angleStep/2)*(_circleradius));
endPoint = CGPointMake(centerPoint.x+sinf(angleStep/2)*(_circleradius-_circlewidth), centerPoint.y+cosf(angleStep/2)*(_circleradius-_circlewidth));
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), startPoint.x, startPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), endPoint.x, endPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
}
- (void)drawSegmentAtCenter:(CGPoint)center from:(CGFloat)startAngle to:(CGFloat)endAngle radius:(CGFloat)radius width:(CGFloat)width doFill:(BOOL)fill{
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:center];
[path addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
if(fill)
[path fill];
[path stroke];
}
@end