Radial gradient background in Swift

Have a look at my implementation of RadialGradientLayer, and feel free to modify it

class RadialGradientLayer: CALayer {

   override init(){

        super.init()

        needsDisplayOnBoundsChange = true
    }

     init(center:CGPoint,radius:CGFloat,colors:[CGColor]){

        self.center = center
        self.radius = radius
        self.colors = colors

        super.init()

    }

    required init(coder aDecoder: NSCoder) {

        super.init()

    }

    var center:CGPoint = CGPointMake(50,50)
    var radius:CGFloat = 20
    var colors:[CGColor] = [UIColor(red: 251/255, green: 237/255, blue: 33/255, alpha: 1.0).CGColor , UIColor(red: 251/255, green: 179/255, blue: 108/255, alpha: 1.0).CGColor]

    override func drawInContext(ctx: CGContext!) {

        CGContextSaveGState(ctx)

        var colorSpace = CGColorSpaceCreateDeviceRGB()

        var locations:[CGFloat] = [0.0, 1.0]

        var gradient = CGGradientCreateWithColors(colorSpace, colors, [0.0,1.0])

        var startPoint = CGPointMake(0, self.bounds.height)
        var endPoint = CGPointMake(self.bounds.width, self.bounds.height)

        CGContextDrawRadialGradient(ctx, gradient, center, 0.0, center, radius, 0)

    }

}

In my case I needed it with two colors only and if you need more colors you need to modify location array declared in drawInContext. Also after creating object from this class don't forget to call its setNeedsDisplay() otherwise it wont work. Also sometimes I needed different size gradients so thats why you have to pass radius parameter in initializer and the center point of your gradient


Nowadays CAGradientLayer is built-in to iOS.

It's this easy:

For years now you simply do this:

class GlowBall: UIView {
    private lazy var pulse: CAGradientLayer = {
        let l = CAGradientLayer()
        l.type = .radial
        l.colors = [ UIColor.red.cgColor,
            UIColor.yellow.cgColor,
            UIColor.green.cgColor,
            UIColor.blue.cgColor]
        l.locations = [ 0, 0.3, 0.7, 1 ]
        l.startPoint = CGPoint(x: 0.5, y: 0.5)
        l.endPoint = CGPoint(x: 1, y: 1)
        layer.addSublayer(l)
        return l
    }()

    override func layoutSubviews() {
        super.layoutSubviews()
        pulse.frame = bounds
        pulse.cornerRadius = bounds.width / 2.0
    }

}
    

enter image description here

The key lines are:

l.colors = [ UIColor.red.cgColor,
                UIColor.yellow.cgColor,
                UIColor.green.cgColor,
                UIColor.blue.cgColor]
l.locations = [ 0, 0.3, 0.7, 1 ]
    

Note that you can change the "stretch" as you wish ...

l.locations = [ 0, 0.1, 0.2, 1 ]
    
    

enter image description here

Use any colors you like

l.colors = [ UIColor.systemBlue.cgColor,
                    UIColor.systemPink.cgColor,
                    UIColor.systemBlue.cgColor,
                    UIColor.systemPink.cgColor,
                    UIColor.systemBlue.cgColor,
                    UIColor.systemPink.cgColor,
                    UIColor.systemBlue.cgColor,
                    UIColor.systemPink.cgColor]
                l.locations = [ 0,0.1,0.2,0.3,0.4,0.5,0.6,1 ]
    

enter image description here

It's really that easy now.

Very useful trick:

Say you want yellow, with a blue band at 0.6:

l.colors = [ UIColor.yellow.cgColor,
                    UIColor.blue.cgColor,
                    UIColor.yellow.cgColor]
                l.locations = [ 0, 0.6, 1 ]
    

That works fine.

    # yellow...
    # blue...
    # yellow...
    

But usually you do this:

    # yellow...
    # yellow...
    # blue...
    # yellow...
    # yellow...
    

Notice there are TWO of the yellows at each end ...

l.colors = [ UIColor.yellow.cgColor,
 UIColor.yellow.cgColor,
 UIColor.blue.cgColor,
 UIColor.yellow.cgColor,
 UIColor.yellow.cgColor]
    

In this way, you can control "how wide" the blue band is:

In this example: the blue band will be narrow and sharp:

l.locations = [ 0, 0.58, 0.6, 0.68, 1 ]
    

In this example the blue band will be broad and soft:

l.locations = [ 0, 0.5, 0.6, 0.7, 1 ]
    

That is really the secret to how you control gradients, and get the look you want.


How to use ...

Notice this is - very simply - a UIView !!

class GlowBall: UIView { ...

Thus simply

  1. In storyboard, place a UIView where you want

  2. In storyboard, change the class to "GlowBall" instead of UIView

You're done!


Here is an implementation in Swift 3 if you're just looking for a UIView radial gradient background:

class RadialGradientLayer: CALayer {

    var center: CGPoint {
        return CGPoint(x: bounds.width/2, y: bounds.height/2)
    }

    var radius: CGFloat {
        return (bounds.width + bounds.height)/2
    }

    var colors: [UIColor] = [UIColor.black, UIColor.lightGray] {
        didSet {
            setNeedsDisplay()
        }
    }

    var cgColors: [CGColor] {
        return colors.map({ (color) -> CGColor in
            return color.cgColor
        })
    }

    override init() {
        super.init()
        needsDisplayOnBoundsChange = true
    }

    required init(coder aDecoder: NSCoder) {
        super.init()
    }

    override func draw(in ctx: CGContext) {
        ctx.saveGState()
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let locations: [CGFloat] = [0.0, 1.0]
        guard let gradient = CGGradient(colorsSpace: colorSpace, colors: cgColors as CFArray, locations: locations) else {
            return
        }
        ctx.drawRadialGradient(gradient, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: radius, options: CGGradientDrawingOptions(rawValue: 0))
    }

}



class RadialGradientView: UIView {

    private let gradientLayer = RadialGradientLayer()

    var colors: [UIColor] {
        get {
            return gradientLayer.colors
        }
        set {
            gradientLayer.colors = newValue
        }
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        if gradientLayer.superlayer == nil {
            layer.insertSublayer(gradientLayer, at: 0)
        }
        gradientLayer.frame = bounds
    }

}

Tags:

Ios

Swift