5

I have this extension to add a Gradient to a View in Swift:

extension UIView {
    func addGradientWithColor(colorTop: UIColor, colorButton: UIColor){
        let gradient = CAGradientLayer()
        gradient.frame = self.bounds
        gradient.colors = [colorTop.cgColor, colorButton.cgColor]

        self.layer.insertSublayer(gradient, at: 0)
    }
}

Then, I use it like that in my UIViewController:

override func viewDidLoad(){
    self.view.addGradientWithColor(colorTop: UIColor.red, colorButton: UIColor.clear)        
    super.viewDidLoad()
}

I run the simulator and it works great. But when I want use my app on a real device, the Gradient does not work.

PD: I tried many ways to do a Gradient but nothing worked on a real device.

Cristik
  • 30,989
  • 25
  • 91
  • 127
Cristian Mora
  • 1,813
  • 2
  • 19
  • 27

5 Answers5

20

The problem is that you are not resizing the layer when the size of the view changes, thus it remains at it's initial value, which mostly depends on how was the view created.

I'd recommend using an UIView subclass, and updating the layer size in the layoutSubviews method, which always gets called when the view resizes:

@IBDesignable
open class GradientView: UIView {
    @IBInspectable
    public var startColor: UIColor = .white {
        didSet {
            gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
            setNeedsDisplay()
        }
    }
    @IBInspectable
    public var endColor: UIColor = .white {
        didSet {
            gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
            setNeedsDisplay()
        }
    }

    private lazy var gradientLayer: CAGradientLayer = {
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = self.bounds
        gradientLayer.colors = [self.startColor.cgColor, self.endColor.cgColor]
        return gradientLayer
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        layer.insertSublayer(gradientLayer, at: 0)
    }

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        layer.insertSublayer(gradientLayer, at: 0)
    }

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

The code above also let's you tweak the start and end colour from Xcode so you have an estimate of how the view will look like when deployed.

Cristik
  • 30,989
  • 25
  • 91
  • 127
7

Try this, it's working for me. Code:

let gradientLayer:CAGradientLayer = CAGradientLayer()
gradientLayer.frame.size = self.gradientView.frame.size
gradientLayer.colors = [UIColor.white.cgColor,UIColor.red.withAlphaComponent(1).cgColor] //Use diffrent colors
gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
self.gradientView.layer.addSublayer(gradientLayer)

For more details explanation see this: Gradient On UIView

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Jaywant Khedkar
  • 5,941
  • 2
  • 44
  • 55
2

Just make sure that you're using color.cgColor instead of color.

baduker
  • 19,152
  • 9
  • 33
  • 56
Amir Ardalan
  • 389
  • 3
  • 7
0

This code for Gradient with transparent background in Swift 5. [transparent|white mix]

    let gradientLayer:CAGradientLayer = CAGradientLayer()
    gradientLayer.colors = [UIColor.white.withAlphaComponent(0.0).cgColor, UIColor.white.cgColor]
    gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0)
    gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
    gradientLayer.frame.size = self.gradientView.frame.size

    self.gradientView.layer.addSublayer(gradientLayer)
Lahiru Pinto
  • 1,621
  • 19
  • 20
0

Using the answers from @Cristik and another user (who's answer I can't find, so if you are that user please let me know so I can give you the credit)

I got this (which updates in storyboard/real device/simulator and allows vertical or horizontal):

Tested on Xcode 11, Swift 5:

@IBDesignable class GradientView: UIView {

    @IBInspectable
    public var firstColor: UIColor = .white {
        didSet {
            gradientLayer.colors = [firstColor.cgColor, secondColor.cgColor]
            setNeedsDisplay()
        }
    }

    @IBInspectable
    public var secondColor: UIColor = .white {
        didSet {
            gradientLayer.colors = [firstColor.cgColor, secondColor.cgColor]
            setNeedsDisplay()
        }
    }

    @IBInspectable var vertical: Bool = true {
        didSet {
            updateGradientDirection()
        }
    }

    lazy var gradientLayer: CAGradientLayer = {
        let layer = CAGradientLayer()
        layer.colors = [firstColor.cgColor, secondColor.cgColor]
        layer.startPoint = CGPoint.zero
        return layer
    }()

    // MARK: - Lifecycle

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

    override init(frame: CGRect) {
        super.init(frame: frame)
        applyGradient()
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        applyGradient()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        updateGradientFrame()
    }

    // MARK: - Helper

    func applyGradient() {
        updateGradientDirection()
        layer.sublayers = [gradientLayer]
    }

    func updateGradientFrame() {
        gradientLayer.frame = bounds
    }

    func updateGradientDirection() {
        gradientLayer.endPoint = vertical ? CGPoint(x: 0, y: 1) : CGPoint(x: 1, y: 0)
    }
}
Akash Kundu
  • 1,278
  • 13
  • 21