How to draw a gradient arc with Core Graphics/iPhone?

后端 未结 3 577
感情败类
感情败类 2020-12-13 15:37

I know how to draw a arc

and I also found how to draw a gradient line from here

I have found two function can draw gradient:CGContextDrawLinearGradient and CGContex
相关标签:
3条回答
  • 2020-12-13 15:54

    You could do it by using CGContextDrawRadialGradient() with a clipping path applied. You could do something like this:

    CGContextBeginPath(context);
    CGContextMoveToPoint(context, startX, startY);
    CGContextAddArcToPoint(context, x1, y1, x2, y2, arcRadius);
    CGConextClosePath(context);
    CGContextClip(context);
    CGContextDrawRadialGradient(context, gradient, startCenter, startRadius, endCenter, endRadius, options);
    
    0 讨论(0)
  • 2020-12-13 16:08

    A swift4 version of Derekahc's answer, and provided more options. check the picture and see what you want~

    private func demoGradientCircle()->UIImage?{
        let imageSize = CGSize(width:1100, height:1100)
        UIGraphicsBeginImageContext(imageSize)
        guard let context = UIGraphicsGetCurrentContext() else{ return nil}
    
        //Set a white background
        context.setFillColor(UIColor.white.cgColor)
        context.fill(CGRect(origin:CGPoint(x:0, y:0), size:imageSize))
        context.setLineWidth(10)
    
        //Set up gradient
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        //  use pure red green blue as the gradient color
        let colorCompoents1:[CGFloat] = [1, 0, 0, 1,
                                         0, 1, 0, 1,
                                         0, 0, 1, 1]
        let locations1:[CGFloat] = [0,0.5,1]
        let gradient1 = CGGradient(colorSpace: colorSpace, colorComponents: colorCompoents1,
                                   locations: locations1, count: locations1.count)!
    
        //Option 1, do linear gradient once, and you'll get the circle on the left side
        let radius:CGFloat = 100
        let centerPointForCircle1 = CGPoint(x:250, y:550)
        context.addArc(center: centerPointForCircle1, radius: radius, startAngle: 0, endAngle:  CGFloat(Double.pi*2), clockwise: true)
        context.replacePathWithStrokedPath()
        context.saveGState()
        context.clip()
        context.drawLinearGradient(gradient1, start: CGPoint(x:150, y:550), end: CGPoint(x:350, y:550), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
    
        //Option 2, if a 3 quarters circle would be enough for you, this will be a better solution(see the circles in the middle), but if you want a perfect gradient circle see Option 3
        context.restoreGState()
        let centerPointForCircle2 = CGPoint(x:550, y:550)
        context.addArc(center: centerPointForCircle2, radius: radius, startAngle: 0, endAngle:  CGFloat(Double.pi*2), clockwise: true)
        context.replacePathWithStrokedPath()
        context.saveGState()
        context.clip()
        let startCenterForCircle2 = CGPoint(x:centerPointForCircle2.x + radius, y:centerPointForCircle2.y)
        let endCenterForCircle2 = CGPoint(x:centerPointForCircle2.x, y:centerPointForCircle2.y - radius / 2)
        context.drawRadialGradient(gradient1, startCenter: startCenterForCircle2, startRadius: radius * 2, endCenter: endCenterForCircle2, endRadius: radius / 2, options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
    
        context.restoreGState()
        let centerPointForCircle3 = CGPoint(x:550, y:850)
        context.addArc(center: centerPointForCircle3, radius: radius, startAngle: CGFloat(-Double.pi*0.9), endAngle:  CGFloat(-Double.pi/2), clockwise: true)
        context.replacePathWithStrokedPath()
        context.saveGState()
        context.clip()
        let startCenterForCircle3 = CGPoint(x:centerPointForCircle3.x + radius, y:centerPointForCircle3.y)
        let endCenterForCircle3 = CGPoint(x:centerPointForCircle3.x, y:centerPointForCircle3.y - radius / 2)
        context.drawRadialGradient(gradient1, startCenter: startCenterForCircle3, startRadius: radius * 2, endCenter: endCenterForCircle3, endRadius: radius / 2, options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
    
        //Option 3, divide the circle into 3 parts(it depends on how many colors you want to use), and do linear gradient respectively. You may have to do some math work to calculate the points.A little bit complex but a perfect gradient circle, see the circle on the right side
        context.restoreGState()
        let centerPointForCircle4 = CGPoint(x:850, y:550)
        context.addArc(center: centerPointForCircle4, radius: radius, startAngle: CGFloat(Double.pi), endAngle:  CGFloat(Double.pi*5/3), clockwise: false)
        context.replacePathWithStrokedPath()
        context.saveGState()
        context.clip()
    
        let colorCompoents2:[CGFloat] = [1, 0, 0, 1,
                                         0, 1, 0, 1]
        let locations2:[CGFloat] = [0,1]
        let gradient2 = CGGradient(colorSpace: colorSpace, colorComponents: colorCompoents2,
                                   locations: locations2, count: locations2.count)!
    
        context.drawLinearGradient(gradient2, start: CGPoint(x:750, y:550), end: CGPoint(x:900, y:463), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
    
        context.restoreGState()
        context.addArc(center: centerPointForCircle4, radius: radius, startAngle: CGFloat(Double.pi*5/3), endAngle:  CGFloat(Double.pi*7/3), clockwise: false)
        context.replacePathWithStrokedPath()
        context.saveGState()
        context.clip()
    
        let colorCompoents3:[CGFloat] = [0, 1, 0, 1,
                                         0, 0, 1, 1]
        let gradient3 = CGGradient(colorSpace: colorSpace, colorComponents: colorCompoents3,
                                   locations: locations2, count: locations2.count)!
        context.drawLinearGradient(gradient3, start: CGPoint(x:900, y:463), end: CGPoint(x:900, y:637), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
    
        context.restoreGState()
        context.addArc(center: centerPointForCircle4, radius: radius, startAngle: CGFloat(Double.pi*7/3), endAngle:  CGFloat(Double.pi), clockwise: false)
        context.replacePathWithStrokedPath()
        context.saveGState()
        context.clip()
    
        let colorCompoents4:[CGFloat] = [0, 0, 1, 1,
                                         1, 0, 0, 1]
        let gradient4 = CGGradient(colorSpace: colorSpace, colorComponents: colorCompoents4,
                                   locations: locations2, count: locations2.count)!
        context.drawLinearGradient(gradient4, start: CGPoint(x:900, y:637), end: CGPoint(x:750, y:550), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
    
        let finalImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    
        return finalImage
    }
    

    enter image description here

    0 讨论(0)
  • 2020-12-13 16:17

    I spent a long time searching for how to do this, too, so I thought I'd post the way I ended up doing it. It turns out both answers are in the excellent answer to this question:

    Draw segments from a circle or donut

    For my purposes, I only used the drawing and gradient parts of that answer. The structure looks more or less like this...

    CGContextRef context = UIGraphicsGetCurrentcontext();
    
    CGFloat arcStartAngle = M_PI;
    CGFloat arcEndAngle = 2 * M_PI;
    
    CGPoint startPoint = CGPointMake(...);
    CGPoint endPoint = CGPointMake(...);
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    CGFloat colors[] =
    {
        1.0, 0.0, 0.0, 1.0,   //RGBA values (so red to green in this case)
        0.0, 1.0, 0.0, 1.0    
    };
    
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, NULL, 2);
    //Where the 2 is for the number of color components. You can have more colors throughout //your gradient by adding to the colors[] array, and changing the components value.
    
    CGColorSpaceRelease(colorSpace);
    
    //Now for the arc part...
    
    CGMutablePathRef arc = CGPathCreateMutable();
    
    CGPathMoveToPoint(arc, NULL, startPoint.x, startPoint.y);
    
    
    //Here, the CGPoint self.arcCenter is the point around which the arc is placed, so maybe the
    //middle of your view. self.radius is the distance between this center point and the arc.
    CGPathAddArc(arc, NULL, self.arcCenter.x, self.arcCenter.y, self.radius, 
                 arcStartAngle, arcEndAngle, YES);
    
    
    //This essentially draws along the path in an arc shape
    CGPathRef strokedArc = CGPathCreateCopyByStrokingPath(arc, NULL, 5.0f, 
                                                          kCGLineCapButt, kCGLineJoinMiter, 10);
    
    
    CGContextSaveGState(context);
    
    CGContextAddPath(context, strokedArc);
    CGContextClip(context);
    
    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
    
    CGContextDrawPath(context, kCGPathFillStroke);
    
    CGGradientRelease(gradient);
    CGContextRestoreGState(context);
    
    //This all draws a gradient that is much larger than the arc itself, but using
    //CGContextClip, it clips out everything EXCEPT the colors in the arc. Saving and Restoring
    //the state allows you to preserve any other drawing going on. If you didn't use these,
    //then all other drawing would also be clipped.
    

    I hope this helps. If any of this is unclear, I recommend you check out the question link above. The answer to that questions contains everything I used in this answer and a few more cool and useful drawing tips.

    0 讨论(0)
提交回复
热议问题