How do I fill a CAShapeLayer() with a gradient and on an angle of 45 degrees?
For example, in Image 1, the below code draws a square and fills the layer blue (UIColor.blueColor().CGColor).
But, how do I fill it with a gradient on a 45 degree angle from blue to red like in Image 2 (i.e. UIColor.blueColor().CGColor to UIColor.redColor().CGColor)?
Code:
let path = UIBezierPath()
path.moveToPoint(CGPoint(x: 0, y: 0))
path.addLineToPoint(CGPoint(x: 0, y: 100))
path.addLineToPoint(CGPoint(x: 100, y: 100))
path.addLineToPoint(CGPoint(x: 100, y: 0))
path.closePath()
let shape = CAShapeLayer()
shape.path = path.CGPath
shape.fillColor = UIColor.blueColor().CGColor

Why not use CAGradientLayer which has startPoint and endPoint properties.
You can do:
import UIKit
import PlaygroundSupport
let frame = CGRect(x: 0, y: 0, width: 100, height: 100)
let view = UIView(frame: frame)
PlaygroundPage.current.liveView = view
let path = UIBezierPath(ovalIn: frame)
let shape = CAShapeLayer()
shape.frame = frame
shape.path = path.cgPath
shape.fillColor = UIColor.blue.cgColor
let gradient = CAGradientLayer()
gradient.frame = frame
gradient.colors = [UIColor.blue.cgColor,
                   UIColor.red.cgColor]
gradient.startPoint = CGPoint(x: 0, y: 1)
gradient.endPoint = CGPoint(x: 1, y: 0)
gradient.mask = shape
view.layer.addSublayer(gradient)

Note: Added a bezier path for a circle, because it would work without the mask for the square.
While the above solutions only really work with trivial angles like 45°, my code is able to set a gradient to any given angle.
public extension CALayer {
    public func applyGradient(of colors: UIColor..., atAngle angle: CGFloat) -> CAGradientLayer {
        let gradient = CAGradientLayer()
        gradient.frame = frame
        gradient.colors = colors
        gradient.calculatePoints(for: angle)
        gradient.mask = self
        return gradient
    }
}
public extension CAGradientLayer {
    /// Sets the start and end points on a gradient layer for a given angle.
    ///
    /// - Important:
    /// *0°* is a horizontal gradient from left to right.
    ///
    /// With a positive input, the rotational direction is clockwise.
    ///
    ///    * An input of *400°* will have the same output as an input of *40°*
    ///
    /// With a negative input, the rotational direction is clockwise.
    ///
    ///    * An input of *-15°* will have the same output as *345°*
    ///
    /// - Parameters:
    ///     - angle: The angle of the gradient.
    ///
    public func calculatePoints(for angle: CGFloat) {
        var ang = (-angle).truncatingRemainder(dividingBy: 360)
        if ang < 0 { ang = 360 + ang }
        let n: CGFloat = 0.5
        let tanx: (CGFloat) -> CGFloat = { tan($0 * CGFloat.pi / 180) }
        switch ang {
        case 0...45, 315...360:
            let a = CGPoint(x: 0, y: n * tanx(ang) + n)
            let b = CGPoint(x: 1, y: n * tanx(-ang) + n)
            startPoint = a
            endPoint = b
        case 45...135:
            let a = CGPoint(x: n * tanx(ang - 90) + n, y: 1)
            let b = CGPoint(x: n * tanx(-ang - 90) + n, y: 0)
            startPoint = a
            endPoint = b
        case 135...225:
            let a = CGPoint(x: 1, y: n * tanx(-ang) + n)
            let b = CGPoint(x: 0, y: n * tanx(ang) + n)
            startPoint = a
            endPoint = b
        case 225...315:
            let a = CGPoint(x: n * tanx(-ang - 90) + n, y: 0)
            let b = CGPoint(x: n * tanx(ang - 90) + n, y: 1)
            startPoint = a
            endPoint = b
        default:
            let a = CGPoint(x: 0, y: n)
            let b = CGPoint(x: 1, y: n)
            startPoint = a
            endPoint = b
        }
    }
}
let layer = CAShapeLayer()
// Setup layer...
// Gradient Direction: →
let gradientLayer1 = layer.applyGradient(of: UIColor.yellow, UIColor.red, at: 0)
// Gradient Direction: ↗︎
let gradientLayer2 = layer.applyGradient(of: UIColor.purple, UIColor.yellow, UIColor.green, at: -45)
// Gradient Direction: ←
let gradientLayer3 = layer.applyGradient(of: UIColor.yellow, UIColor.blue, UIColor.green, at: 180)
// Gradient Direction: ↓
let gradientLayer4 = layer.applyGradient(of: UIColor.red, UIColor.blue, at: 450)
So I actually just recently spent a lot of time trying to answer this myself. Here are some example angles just to help understand and visualize the clockwise direction of rotation.

If you are interested in how I figured it out, I made a table to visualize essentially what I am doing from 0° - 360°.

I think it's
shape.startPoint = CGPoint(x: 1.0, y: 0.0)
shape.endPoint = CGPoint(x: 0.0, y: 1.0)
, which is the first color at the bottom-right to the second color at the top-left. If you want the first color at the top-right and second color at the bottom-left, then you should have
shape.startPoint = CGPoint(x: 1.0, y: 1.0)
shape.endPoint = CGPoint(x: 0.0, y: 0.0)
First color at top-left, second color at bottom-right
shape.startPoint = NSMakePoint(x: 0.0, y: 1.0)
shape.endPoint = NSMakePoint(x: 1.0, y: 0.0)
first color at bottom-left, second color at top-right
shape.startPoint = CGPoint(x: 0.0, y: 0.0)
shape.endPoint = CGPoint(x: 1.0, y: 1.0)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With