iosswiftquartz-graphicsquartz-core

How to make a CAGradientLayer follow a CGPath


Given a CAShapeLayer defining a path as in the picture below, I want to add a CAGradientLayer that follows the path of the shape layer.

For example, given a gradient from black->red:

The Green should become the gradient

Every previous post I've found does not actually answer this question. For example, because I can only add axial CAGradientLayers, I can kind-of do this (pic below), but you can see it's not correct (the top left ends up becoming black again). How do I make the gradient actually follow the path/mask

enter image description here


Solution

  • For the simple circular shape path, the new conic gradient is great. I was able to get this immediately (this is a conic gradient layer masked by a circle shape layer):

    enter image description here

    I used red and green so as to show the gradient clearly. This doesn't seem to be identically what you're after, but I can't believe it will be very difficult to achieve your goals now that .conic exists.

    class MyGradientView : UIView {
        override class var layerClass : AnyClass { return CAGradientLayer.self }
        required init?(coder aDecoder: NSCoder) {
            super.init(coder:aDecoder)
            let lay = self.layer as! CAGradientLayer
            lay.type = .conic
            lay.startPoint = CGPoint(x:0.5,y:0.5)
            lay.endPoint = CGPoint(x:0.5,y:0)
            lay.colors = [UIColor.green.cgColor, UIColor.red.cgColor]
        }
        override func layoutSubviews() {
            super.layoutSubviews()
            let shape = CAShapeLayer()
            shape.frame = self.bounds
            shape.strokeColor = UIColor.black.cgColor
            shape.fillColor = UIColor.clear.cgColor
            let b = UIBezierPath(ovalIn: shape.frame.inset(by: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)))
            shape.path = b.cgPath
            shape.lineWidth = 10
            shape.lineCap = .round
            shape.strokeStart = 0.1
            shape.strokeEnd = 0.9
            shape.anchorPoint = CGPoint(x: 0.5, y: 0.5)
            shape.transform = CATransform3DMakeRotation(-.pi/2, 0, 0, 1)
            self.layer.mask = shape
        }
    }