I was trying to reproduce the overlapping circle Apple made for the application "Activity". (See image below).
If you use standard Bezier Path the staring/ending position will have effects only between 0 and 2PI. If you try, for example, to fill for 4PI (even using some shadows) you cannot simulate an overlapping loading.
How is possible to make something similar to the Apple solution to create an overlapping circle?
This shows the general idea--transform is definitely the way to go. You'll have to adjust the sizes, arrows, and shadows for what you need. But the view allow the tint and angle to be adjusted from Interface Builder. This should get you going.
#import <UIKit/UIKit.h>
IB_DESIGNABLE
@interface ActivityCircleView : UIView
@property (nonatomic) IBInspectable CGFloat angleOfRotationInDegrees;
@end
#import "ActivityCircleView.h"
@implementation ActivityCircleView
- (void)setAngleOfRotation:(CGFloat)angleOfRotationInDegrees {
_angleOfRotationInDegrees = angleOfRotationInDegrees;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
[self drawActivityWithTintColor:self.tintColor angleOfRotationInDegrees:self.angleOfRotationInDegrees];
}
- (void)drawActivityWithTintColor: (UIColor*)tintColor angleOfRotationInDegrees: (CGFloat)angleOfRotationInDegrees
{
//// General Declarations
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext();
//// Color Declarations
CGFloat tintColorRGBA[4];
[tintColor getRed: &tintColorRGBA[0] green: &tintColorRGBA[1] blue: &tintColorRGBA[2] alpha: &tintColorRGBA[3]];
UIColor* brighter = [UIColor colorWithRed: (tintColorRGBA[0] * 0.5 + 0.5) green: (tintColorRGBA[1] * 0.5 + 0.5) blue: (tintColorRGBA[2] * 0.5 + 0.5) alpha: (tintColorRGBA[3] * 0.5 + 0.5)];
//// Gradient Declarations
CGFloat gradientLocations[] = {0, 0.73};
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)@[(id)tintColor.CGColor, (id)brighter.CGColor], gradientLocations);
//// Shadow Declarations
NSShadow* shadow = [[NSShadow alloc] init];
[shadow setShadowColor: UIColor.blackColor];
[shadow setShadowOffset: CGSizeMake(0.1, 0.1)];
[shadow setShadowBlurRadius: 5];
//// Activity Circle
{
//// Circle With Overlapping Shadow
{
CGContextSaveGState(context);
CGContextTranslateCTM(context, 50, 50);
CGContextRotateCTM(context, -angleOfRotationInDegrees * M_PI / 180);
//// Left Half Circle Drawing
UIBezierPath* leftHalfCirclePath = UIBezierPath.bezierPath;
[leftHalfCirclePath moveToPoint: CGPointMake(0, -40)];
[leftHalfCirclePath addCurveToPoint: CGPointMake(0, -20) controlPoint1: CGPointMake(0, -40) controlPoint2: CGPointMake(0, -31.68)];
[leftHalfCirclePath addCurveToPoint: CGPointMake(-7.44, -18.57) controlPoint1: CGPointMake(-2.63, -20) controlPoint2: CGPointMake(-5.14, -19.49)];
[leftHalfCirclePath addCurveToPoint: CGPointMake(-20, -0) controlPoint1: CGPointMake(-14.8, -15.62) controlPoint2: CGPointMake(-20, -8.42)];
[leftHalfCirclePath addCurveToPoint: CGPointMake(0, 20) controlPoint1: CGPointMake(-20, 11.05) controlPoint2: CGPointMake(-11.05, 20)];
[leftHalfCirclePath addCurveToPoint: CGPointMake(0, 40) controlPoint1: CGPointMake(0, 27.41) controlPoint2: CGPointMake(0, 34.35)];
[leftHalfCirclePath addCurveToPoint: CGPointMake(-40, -0) controlPoint1: CGPointMake(-22.09, 40) controlPoint2: CGPointMake(-40, 22.09)];
[leftHalfCirclePath addCurveToPoint: CGPointMake(-24.08, -31.94) controlPoint1: CGPointMake(-40, -13.05) controlPoint2: CGPointMake(-33.75, -24.64)];
[leftHalfCirclePath addLineToPoint: CGPointMake(-23.84, -32.13)];
[leftHalfCirclePath addCurveToPoint: CGPointMake(0, -40) controlPoint1: CGPointMake(-17.18, -37.07) controlPoint2: CGPointMake(-8.93, -40)];
[leftHalfCirclePath addLineToPoint: CGPointMake(0, -40)];
[leftHalfCirclePath closePath];
[tintColor setFill];
[leftHalfCirclePath fill];
//// Circle With Shadow Drawing
UIBezierPath* circleWithShadowPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(-10, 20, 20, 20)];
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, shadow.shadowOffset, shadow.shadowBlurRadius, [shadow.shadowColor CGColor]);
[brighter setFill];
[circleWithShadowPath fill];
CGContextRestoreGState(context);
//// Right Half Circle Drawing
UIBezierPath* rightHalfCirclePath = UIBezierPath.bezierPath;
[rightHalfCirclePath moveToPoint: CGPointMake(40, -0)];
[rightHalfCirclePath addCurveToPoint: CGPointMake(0, 40) controlPoint1: CGPointMake(40, 22.09) controlPoint2: CGPointMake(22.09, 40)];
[rightHalfCirclePath addCurveToPoint: CGPointMake(0, 20) controlPoint1: CGPointMake(0, 33.83) controlPoint2: CGPointMake(0, 27.02)];
[rightHalfCirclePath addCurveToPoint: CGPointMake(20, -0) controlPoint1: CGPointMake(11.05, 20) controlPoint2: CGPointMake(20, 11.05)];
[rightHalfCirclePath addCurveToPoint: CGPointMake(0, -20) controlPoint1: CGPointMake(20, -11.05) controlPoint2: CGPointMake(11.05, -20)];
[rightHalfCirclePath addCurveToPoint: CGPointMake(0, -40) controlPoint1: CGPointMake(0, -28.3) controlPoint2: CGPointMake(0, -35.35)];
[rightHalfCirclePath addCurveToPoint: CGPointMake(40, -0) controlPoint1: CGPointMake(22.09, -40) controlPoint2: CGPointMake(40, -22.09)];
[rightHalfCirclePath closePath];
CGContextSaveGState(context);
[rightHalfCirclePath addClip];
CGContextDrawLinearGradient(context, gradient, CGPointMake(20, -40), CGPointMake(20, 40), 0);
CGContextRestoreGState(context);
CGContextRestoreGState(context);
}
//// Arrow
{
//// Arrow Line Drawing
UIBezierPath* arrowLinePath = UIBezierPath.bezierPath;
[arrowLinePath moveToPoint: CGPointMake(43.5, 20.5)];
[arrowLinePath addLineToPoint: CGPointMake(57.5, 20.5)];
arrowLinePath.lineCapStyle = kCGLineCapRound;
[UIColor.blackColor setStroke];
arrowLinePath.lineWidth = 4;
[arrowLinePath stroke];
//// Arrow Head Drawing
UIBezierPath* arrowHeadPath = UIBezierPath.bezierPath;
[arrowHeadPath moveToPoint: CGPointMake(50.8, 14.5)];
[arrowHeadPath addLineToPoint: CGPointMake(57.5, 20.5)];
[arrowHeadPath addLineToPoint: CGPointMake(50.8, 26.5)];
arrowHeadPath.lineCapStyle = kCGLineCapRound;
[UIColor.blackColor setStroke];
arrowHeadPath.lineWidth = 4;
[arrowHeadPath stroke];
}
}
//// Cleanup
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
@end
By the way, I cheated a bit and used PaintCode.
Here's my attempt:
It's generated entirely in code (I had to use the Angle Gradient Layer to get the gradient, but the rest of the code is mine). I can easily obtain any desired angle just by applying a rotation transform:
Of course I also hard-coded all the values, because I was just trying it out, but feel free to play with it:
let con = UIGraphicsGetCurrentContext()
let c1 = UIColor(red: 0.8, green: 0.8, blue: 0.7, alpha: 1).CGColor
let c2 = UIColor(red: 0.8, green: 0.8, blue: 0.1, alpha: 1).CGColor
let outer:CGFloat = 20
let diff:CGFloat = 20
let inner:CGFloat = outer + diff
func circle(inset:CGFloat) {
CGContextAddEllipseInRect(con, rect.rectByInsetting(
dx: inset, dy: inset))
}
CGContextSaveGState(con)
circle(outer); circle(inner); CGContextEOClip(con)
let im = AngleGradientLayer.newImageGradientInRect(
rect, colors:[c1,c2], locations:[0,1]).takeRetainedValue()
CGContextDrawImage(con, rect, im)
CGContextRestoreGState(con)
circle(outer)
CGContextStrokePath(con)
circle(inner)
CGContextStrokePath(con)
let (_,lower) = rect.rectsByDividing(rect.height / 2, fromEdge: .MinYEdge)
CGContextAddRect(con, lower)
CGContextEOClip(con)
CGContextSetFillColorWithColor(con, c1)
CGContextSaveGState(con)
CGContextSetShadowWithColor(con, CGSizeMake(0,3), 6,
UIColor.blackColor().colorWithAlphaComponent(0.7).CGColor)
let top = lower.origin.y - diff/2
let left = rect.width - diff * 2
circle(outer); circle(inner); CGContextEOClip(con)
CGContextFillEllipseInRect(con, CGRectMake(left,top,diff,diff))
CGContextRestoreGState(con)
CGContextStrokeEllipseInRect(con, CGRectMake(left,top,diff,diff))