Train of thought

  • There are many ways to draw a static circle, such as UIBezierPath or UIBezierPath + CAShapeLayer, etc., which will be used in this article.

  • The dynamic effect is to constantly update the Angle (radian) of the circle, and then constantly draw the circle to achieve the dynamic effect, using Quartz2d to draw in drawRect

  • CGGradientRef CGContextDrawLinearGradient circle, parameters set the gradient, the startPoint and set the endPoint gradient direction

  • Package to a custom View, quick to use (Demo at the end of this article)

The final result

Manual drawing

Use:

TYCircleViewConfigure *configure = [[TYCircleViewConfigure alloc]init]; configure.lineColor = [UIColor lightGrayColor]; / / circle background configure circleLineWidth = 10; Configure. isClockwise = YES; // Set a clockwise circle configure.startPoint = CGPointMake(width / 2, 0); configure.endPoint = CGPointMake(width / 2 , width); Configure.colorarr = @[(id)TYColorFromRGB(0x349CF7).cgcolor,// Light blue (id)TYColorFromRGB(0xFE5858).cgcolor,// Deep orange (id)TYColorFromRGB(0x72DC4F).cgcolor // light green]; // Each color region value configure.colorSize = @[@0,@0.3,@0.8]; TYCircleView *circleView = [[TYCircleView alloc]initWithFrame:CGRectMake(x , y, width, width) configure:configure]; [self.view addSubview:circleView];Copy the code

Automatic drawing

  • To automatically draw the ring, you only need to combine the value of the slider and the value of the ring center label, that is, dynamically change the value of the ring center label
  • How to dynamically change the value of the label, recommended wheel :UICountingLabel, the specific implementation is relatively simple, no longer introduced, combined with a hole can not be overstep, welcome to comment below the message ~

The main process

1. Create a configuration class for the required attributes in the. H file

/ / configuration properties / * * * * * * * * * * * * * * * * * * * * * TYCircleViewConfigure * * * * * * * * * * * * * * * * * * * * * * * * / @ interface TYCircleViewConfigure: NSObject /** circleLineWidth */ @property (nonatomic, assign) CGFloat circleLineWidth; / / @property (nonatomic, strong) UIColor *lineColor; /** clockwise The default is NO: counterclockwise */ @property (nonatomic, assign) BOOL isClockwise; /** property (nonatomic, assign) CGPoint startPoint; */ @property (nonatomic, assign) CGPoint endPoint; */ @property (nonatomic, strong) NSArray *colorArr; @property (nonatomic, strong) NSArray *colorSize; // Note: colorarr. count and colorsize. count must be equalCopy the code
  1. Draw a gray background circle
UIBezierPath creates a circle path // CGPoint arcCenter = CGPointMake (self. Frame. The size, width / 2.0, the self. The frame. The size, height / 2.0); / / radius, circleLineWidth = circle radius, line width CGFloat = (self. Frame. The size, width - _configure. CircleLineWidth) / 2.0; // start arc: -m_pi_2, vertical upward; -M_PI_2 + M_PI*2, circling clockwise //clockwise = NO Circle, circle disappeared zero UIBezierPath * = [UIBezierPath bezierPathWithArcCenter: arcCenter radius: the radius startAngle: - M_PI_2 endAngle:-M_PI_2 + M_PI*2 clockwise:YES]; //2. Create ShapeLayer CAShapeLayer *bgLayer = [CAShapeLayer layer];
    bgLayer.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.width);
    bgLayer.fillColor = [UIColor clearColor].CGColor; // Ring path fill colorbgLayer.lineWidth = _configure.circleLineWidth; // Ring widthbgLayer.strokeColor = _configure.lineColor.CGColor; // Path colorbgLayer.strokeStart = 0.f; // Path start positionbgLayer.strokeEnd = 1.f; // Path end locationbgLayer.path = circle.CGPath; //3. Set path to layer, replace background ring [self.layer addSublayer:bgLayer];
    
}
Copy the code
  • The radians at the beginning of the radians (the beginning and the end radians) can be represented by a graph that shows the radians going clockwise, so if it’s counterclockwise, -m_pi_2 means straight up

3. In the implementation of TYDrawCircleView, rewrite drawRect to draw the circle

The rect - (void) drawRect: (CGRect) {/ / get the graphics context CGContextRef CTX = UIGraphicsGetCurrentContext (); / / set the line width CGContextSetLineWidth (CTX, _configure circleLineWidth); CGContextSetLineCap(CTX, kCGLineCapRound); / / set the brush color CGContextSetFillColorWithColor (CTX, [UIColor blackColor]. CGColor); // Set the center of the circle CGFloat originX = rect.sie.width / 2; CGFloat originY = rect.size.height / 2; / / CGFloat calculating radius radius = MIN (originX, originY) - _configure. CircleLineWidth / 2.0; CGFloat minAngle = M_PI/90 - self.progress * M_PI/80; // Create a minimum initial arc value to avoid circle disappearance when progress is 0 or 1. // Draw an arc counterclockwiseif (self.configure.isClockwise) {

        CGContextAddArc(ctx, rect.size.width / 2, rect.size.height / 2, radius, -M_PI_2, -M_PI_2 + minAngle + (2 * M_PI)*self.progress, NO);
    }else{ CGContextAddArc(ctx, rect.size.width / 2, rect.size.height / 2, radius, -M_PI_2, -M_PI_2 - minAngle + (2 * M_PI)*(1-self.progress), YES); } / / 2. Create a gradient CGFloat locations [_configure. ColorSize. Count];for (NSInteger index = 0; index < _configure.colorSize.count; index++) {
        locations[index] = [_configure.colorSize[index] floatValue]; } / / create the RGB color space, and create the future, in the context of the colors are expressed in RGB CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB (); CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,(__bridge CFArrayRef _Nonnull)_configure.colorArr, _configure.colorSize.count==0? NULL:locations); CGColorSpaceRelease(colorSpace); colorSpace = NULL; / / 3. Draw a circle path CGContextReplacePathWithStrokedPath (CTX); // Clipping path CGContextClip(CTX); / / 4. Use gradient, change the direction of the fill color CGContextDrawLinearGradient (CTX, gradient, _configure. The startPoint _configure. The endPoint, 1); // Release CGGradientRelease(gradient); }Copy the code
  1. Dynamic circle
  • Use slider to manually change its value and associate the slider’s value with progress. When progress changes:
- (void)setProgress:(CGFloat)progress {
    _progress = progress;
    _circleView.progress = progress;
  
    [_circleView setNeedsDisplay]; TYDrawCircleView DrawRect {// call TYDrawCircleView DrawRect}Copy the code

Gradient explanation

  • Gradient direction

Our heavy DrawRect method in TYDrawCircleView CGContextDrawLinearGradient method to draw a circle, and set the circle of the gradient direction of gradient and the gradient distribution, the startPoint position, the starting point of the gradient, An endPoint is the endPoint of a gradient

//width = circle diameter configure.startPoint = CGPointMake(width / 2, 0); configure.endPoint = CGPointMake(width / 2 , width); // Gradient distribution directionCopy the code

Set tartPoint = CGPointMake(width / 2, 0) configure. EndPoint = CGPointMake(width / 2, width

  • Gradient distribution area

We set up three gradients in use

Configure.colorarr = @[(id)TYColorFromRGB(0x349CF7).cgcolor,// Light blue (id)TYColorFromRGB(0xFE5858).cgcolor,// dark orange (id)TYColorFromRGB(0x72DC4F).cgcolor // light green];Copy the code

ColorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize = configure.colorSize

// Each color region value configure.colorSize = @[@0,@0.3,@0.8];Copy the code

Note:

1. Configure. colorSize The number of elements in the array must be the same as configure.colorArr, that is, the number of colors and the value of the color region must correspond one by one; otherwise, the gradient effect may be different from that expected

2. Configure. colorSize Each element in the array represents the starting position of the corresponding color from the distribution direction (the Y-axis direction in this demo). The value of the entire region is 1, and the color region must be between 0 and 1 to be valid

3. If the elements in the configure.colorSize array are not arranged in a certain size order, the colors will be out of order

conclusion

1. Comments in other explanatory codes are also detailed. If you have any questions, please leave a message and communicate with me

2. The Demo address: github.com/TynnPassBy/…

3. Code word is not easy, if it helps you, please give a thumbs-up