在具有圆角边缘的弧形中绘制渐变颜色

Drawing a gradient color in an arc with a rounded edge

我想在 Swift 中复制这个。进一步阅读:How to draw a linear gradient arc with Qt QPainter?

我已经想出如何在矩形视图内绘制渐变:

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)
    }

以及如何画圆弧

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)
    }

随着渐变,它占据了整个视图。我不确定如何将渐变限制到特定区域,所以我制作了 UIView 的子类,修改了框架并使用了渐变代码(第一个片段)。

对于弧线,我使用了第二个片段。我在想我可以画一个圆弧,然后在圆弧的末端画一个圆来平滑圆弧。我该怎么做并在弧形上绘制渐变?

请忽略紫色标记。第一张图片是我指定的渐变弧的开始,第二张是渐变弧的结尾,在开始弧的下方和左侧。

如何使用 Core Graphics 在 Swift 中绘制它?

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

}

希望对您有所帮助!!

2019 年的精确代码,具有真正的圆形渐变和完全灵活的结束位置:

这些天基本上你有一个渐变层和该层的蒙版。

所以第一步,你需要一个“面具”

iOS 中的“面具”到底是什么?

在iOS中,你实际上使用了一个CAShapeLayer来制作遮罩。

这令人困惑。它应该被称为 'CAShapeToUseAsAMaskLayer'

但这就是你的做法,一个 CAShapeLayer。

第二步。在布局中,您必须调整形状层的大小。就是这样。

所以基本上就是这样:

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
    }()
}

这就是全部。

请注意,渐变层的遮罩确实设置为形状层,我们将其命名为“arcLayer”。

形状图层用作遮罩。

这是两层的确切代码:

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{[=11=].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
}()

同样,请注意 arcLayer 是一个形状层,您正在 用作遮罩 - 您实际上并未将其添加为子视图。

请记住,用作遮罩的形状层 不会! 被添加为子视图。

这可能会造成混淆。它根本不是一个“层”。

最后,注意 arcLayer 使用路径 arcPath

完美构建路径,使 strokeStart 和 strokeEnd 正常工作。

您想完美地构建路径。

因此非常容易设置笔划起始值和笔划终止值。

它们完全正确 运行 顺时针,从顶部,从 0 到 1。

这是具体的代码:

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
}()

大功告成!