Loader Covering Circle

I was trying to reproduce the overlapping circle Apple created for the Activity app. (See image below).

enter image description here

If you are using the standard Bezier Path, the look / end position will only have effects between 0 and 2PI. If you try, for example, to fill the 4PI (even using some shadows), you will not be able to simulate overlapping loading.

How can you do something similar to Apple's solution to create an overlapping circle?

+3


source to share


2 answers


This shows the general idea - transformation is definitely the way to go. You will need to adjust the dimensions, arrows and shadows to whatever you need. But the view allows you to adjust the hue and angle from the Interface Builder. This should get you going.

enter image description here



#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 little and used PaintCode.

+4


source


Here's my attempt:

enter image description here

It is generated entirely in code (I needed to use the Angle Gradient Layer to get the gradient, but the rest of the code is mine). I can easily get any angle I want just by applying a rotation transform:



enter image description here

Of course, I also coded all the values, because I was just trying to do this, 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))

      

+3


source







All Articles