Drawing a gradient color in an arc with a rounded edge

梦想与她 提交于 2020-01-23 17:36:08

问题


I want to replicate this in Swift.. reading further: How to draw a linear gradient arc with Qt QPainter?

I've figured out how to draw a gradient inside of a rectangular view:

override func drawRect(rect: CGRect) {
        let currentContext = UIGraphicsGetCurrentContext()
        CGContextSaveGState(currentContext)
        let colorSpace = CGColorSpaceCreateDeviceRGB()

        let startColor = MyDisplay.displayColor(myMetric.currentValue)
        let startColorComponents = CGColorGetComponents(startColor.CGColor)
        let endColor = MyDisplay.gradientEndDisplayColor(myMetric.currentValue)
        let endColorComponents = CGColorGetComponents(endColor.CGColor)

        var colorComponents
            = [startColorComponents[0], startColorComponents[1], startColorComponents[2], startColorComponents[3], endColorComponents[0], endColorComponents[1], endColorComponents[2], endColorComponents[3]]

        var locations: [CGFloat] = [0.0, 1.0]
        let gradient = CGGradientCreateWithColorComponents(colorSpace, &colorComponents, &locations, 2)

        let startPoint = CGPointMake(0, 0)
        let endPoint = CGPointMake(1, 8)

        CGContextDrawLinearGradient(currentContext, gradient, startPoint, endPoint, CGGradientDrawingOptions.DrawsAfterEndLocation)
        CGContextRestoreGState(currentContext)
    }

and how to draw an arc

func drawArc(color: UIColor, x: CGFloat, y: CGFloat, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, lineWidth: CGFloat, clockwise: Int32) {
        let context = UIGraphicsGetCurrentContext()
        CGContextSetLineWidth(context, lineWidth)
        CGContextSetStrokeColorWithColor(context,
                                         color.CGColor)
        CGContextAddArc(context, x, y, radius, startAngle, endAngle, clockwise)
        CGContextStrokePath(context)
    }

With the gradient, that took up the entire view. I was unsure how to limit the gradient to a particular area so I made a subclass of UIView, modified the frame and used the gradient code (first snippet).

For arcs, I've used the second snippet. I'm thinking I can draw an arc and then draw a circle at the end of the arc to smooth out the arc with a rounded finish. How can I do this and draw a gradient over the arc shape?

Please ignore the purple mark. The first picture is the beginning of the gradient arc from my specification, the second is the end of the gradient arc, below and to the left of the beginning arc.

How can I draw this in Swift with Core Graphics?


回答1:


override func draw(_ rect: CGRect) {

    //Add gradient layer
    let gl = CAGradientLayer()
    gl.frame = rect
    gl.colors = [UIColor.red.cgColor, UIColor.blue.cgColor]
    layer.addSublayer(gl)

    //create mask in the shape of arc
    let sl = CAShapeLayer()
    sl.frame = rect
    sl.lineWidth = 15.0
    sl.strokeColor = UIColor.red.cgColor
    let path = UIBezierPath()
    path.addArc(withCenter: .zero, radius: 250.0, startAngle: 10.0.radians, endAngle: 60.0.radians, clockwise: true)
    sl.fillColor = UIColor.clear.cgColor
    sl.lineCap = kCALineCapRound
    sl.path = path.cgPath

    //Add mask to gradient layer
    gl.mask = sl

}

Hope this helps!!




回答2:


Exact code for 2019 with true circular gradient and totally flexible end positions:

These days basically you simply have a gradient layer and mask for that layer.

You actually use a CAShapeLayer to make a mask. (Which I find confusing: it should be called something like "CAShapeToUseAsMaskLayer".)

In layout, you must resize the shape layer. And that's it.

So basically it's just this:

class SweetArc: UIView {

    open override func layoutSubviews() {
        super.layoutSubviews()
        arcLayer.frame = bounds
        gradientLayer.frame = bounds
    }

    private lazy var gradientLayer: CAGradientLayer = {
        let l = CAGradientLayer()
        ...
        l.mask = arcLayer
        layer.addSublayer(l)
        return l
    }()

    private lazy var arcLayer: CAShapeLayer = {
        let l = CAShapeLayer()
        ...
        return l
    }()
}

That's the whole thing.

Note that the mask of the gradient layer is indeed set as the shape layer, which we have named "arcLayer".

A shape layer is used as a mask.

Here's the exact code for the two layers:

private lazy var gradientLayer: CAGradientLayer = {
    // recall that .conic is finally available in 12 !
    let l = CAGradientLayer()
    l.type = .conic
    l.colors = [
        UIColor.yellow,
        UIColor.red
        ].map{$0.cgColor}
    l.locations = [0,  1]
    l.startPoint = CGPoint(x: 0.5, y: 0.5)
    l.endPoint = CGPoint(x: 0.5, y: 0)
    l.mask = arcLayer
    layer.addSublayer(l)
    return l
}()

private lazy var arcLayer: CAShapeLayer = {
    let l = CAShapeLayer()
    l.path = arcPath

    // to use a shape layer as a mask, you mask with white-on-clear:
    l.fillColor = UIColor.clear.cgColor
    l.strokeColor = UIColor.white.cgColor

    l.lineWidth = lineThick
    l.lineCap = CAShapeLayerLineCap.round
    l.strokeStart = 0.4 // try values 0-1
    l.strokeEnd = 0.5 // try values 0-1
    return l
}()

Again, notice arcLayer is a shape layer you are using as a mask - you do NOT actually add it as a subview.

Finally, notice arcLayer uses a path arcPath.

Construct the path perfectly so that strokeStart and strokeEnd work correctly.

You want to build the path perfectly so that it is very easy to set the stroke start and stroke end values, and they make sense perfectly and correctly run clockwise from the top from 0 to 1.

Here's the exact code for that:

private let lineThick: CGFloat = 10.0
private let outerGap: CGFloat = 10.0
private let beginFraction: CGFloat = 0  // 0 means from top; runs clockwise

private lazy var arcPath: CGPath = {
    let b = beginFraction * .pi * 2.0
    return UIBezierPath(
        arcCenter: bounds.centerOfCGRect(),
        radius: bounds.width / 2.0 - lineThick / 2.0 - outerGap,
        startAngle: .pi * -0.5 + b,
        endAngle: .pi * 1.5 + b,
        clockwise: true
    ).cgPath
}()

You're done!



来源:https://stackoverflow.com/questions/37092248/drawing-a-gradient-color-in-an-arc-with-a-rounded-edge

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!