17

I'm having trouble trying to add a gradient background to UIView. I made an extension of UIView and added the following method:

func setGradientBackground(colorTop: UIColor, colorBottom: UIColor) {
    let gradientLayer = CAGradientLayer()
    gradientLayer.colors = [colorTop, colorBottom]
    gradientLayer.frame = bounds

    layer.insertSublayer(gradientLayer, at: 0)
}

Then I call:

separatorView.setGradientBackground(colorTop: .clear, colorBottom: .red)

But it doesn't work. The view is presented but its background is entirely clear. I tried as well with CGColor

2
  • 2
    try this, github.com/matvdg/Gradient-View Commented Apr 26, 2018 at 7:27
  • the code works fine, check the rest of your code Commented Apr 26, 2018 at 7:58

7 Answers 7

20

There were 2 problems:

  • I needed to set the start and end points to provide a gradient direction:

    func setGradientBackground(colorTop: UIColor, colorBottom: UIColor) {
        let gradientLayer = CAGradientLayer()
        gradientLayer.colors = [colorBottom.cgColor, colorTop.cgColor]
        gradientLayer.startPoint = CGPoint(x: 0.5, y: 1.0)
        gradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0)
        gradientLayer.locations = [0, 1]
        gradientLayer.frame = bounds
    
       layer.insertSublayer(gradientLayer, at: 0)
    }
    
  • And the second issue was that CAGradientLayer takes effect after the view is layed out. I solved that calling setGradientBackground() on viewDidAppear:

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        separatorView.setGradientBackground(colorTop: .clear, colorBottom: Colors.darkGrey)
    }
    
Sign up to request clarification or add additional context in comments.

2 Comments

why u actually accepted your answer, instead of Moayad's? seems like he answered faster.
Re second issue: it's actually because the view geometry is set when viewDidAppear(_:) is called. You can, and probably should, call setGradientBackground(colorTop:colorBottom:) elsewhere, for example viewDidLoad(), otherwise you could be adding multiple gradient layers. You just need to make sure to update the gradient layer frame whenever the geometry is set in viewDidLayoutSubviews().
16

Update your method to the code blow:

func setGradientBackground(colorTop: UIColor, colorBottom: UIColor){
    let gradientLayer = CAGradientLayer()
    gradientLayer.colors = [colorTop.cgColor, colorBottom.cgColor]
    gradientLayer.startPoint = CGPoint(x: 0.5, y: 1.0)
    gradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0)
    gradientLayer.locations = [NSNumber(floatLiteral: 0.0), NSNumber(floatLiteral: 1.0)]
    gradientLayer.frame = self.bounds

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

Comments

12

Moayad's answer worked best for me but it was backwards with top color as bottom so i reversed the function he posted.

Swift 4

    func setGradientBackground(colorTop: UIColor, colorBottom: UIColor){
        let gradientLayer = CAGradientLayer()
        gradientLayer.colors = [colorBottom.cgColor, colorTop.cgColor]
        gradientLayer.startPoint = CGPoint(x: 0.5, y: 1.0)
        gradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0)
        gradientLayer.locations = [0, 1]
        gradientLayer.frame = bounds

        layer.insertSublayer(gradientLayer, at: 0)
}

Comments

6

For @IBDesignable: You can custom your UI on storyboard. It is easier to use.

@IBDesignable class GradientView: UIView {

    @IBInspectable var startColor: UIColor = .blue {
        didSet {
            setNeedsLayout()
        }
    }

    @IBInspectable var endColor: UIColor = .green {
        didSet {
            setNeedsLayout()
        }
    }

    @IBInspectable var shadowColor: UIColor = .yellow {
        didSet {
            setNeedsLayout()
        }
    }

    @IBInspectable var shadowX: CGFloat = 0 {
        didSet {
            setNeedsLayout()
        }
    }

    @IBInspectable var shadowY: CGFloat = -3 {
        didSet {
            setNeedsLayout()
        }
    }

    @IBInspectable var shadowBlur: CGFloat = 3 {
        didSet {
            setNeedsLayout()
        }
    }

    @IBInspectable var startPointX: CGFloat = 0 {
        didSet {
            setNeedsLayout()
        }
    }

    @IBInspectable var startPointY: CGFloat = 0 {
        didSet {
            setNeedsLayout()
        }
    }

    @IBInspectable var endPointX: CGFloat = 0 {
        didSet {
            setNeedsLayout()
        }
    }

    @IBInspectable var endPointY: CGFloat = 0 {
        didSet {
            setNeedsLayout()
        }
    }

    @IBInspectable var cornerRadius: CGFloat = 0 {
        didSet {
            setNeedsLayout()
        }
    }

    override class var layerClass: AnyClass {
        return CAGradientLayer.self
    }

    override func layoutSubviews() {
        let gradientLayer = layer as! CAGradientLayer
        gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
        gradientLayer.startPoint = CGPoint(x: startPointX, y: startPointY)
        gradientLayer.endPoint = CGPoint(x: endPointX, y: endPointY)
        layer.cornerRadius = cornerRadius
        layer.shadowColor = shadowColor.cgColor
        layer.shadowOffset = CGSize(width: shadowX, height: shadowY)
        layer.shadowRadius = shadowBlur
        layer.shadowOpacity = 1
    }
}

1 Comment

Hi, Can you please explain that how to set Start X, start Y, End X and End Y. ???
3

The code works fine. However, you should call it at layoutSubviews.

Swift 5

class ViewExample: UIView {

    // MARK: - Lifecycle
    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

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

    func configureGradientLayer(){
        backgroundColor = .clear
        let gradient = CAGradientLayer()
        gradient.colors = [UIColor.systemPurple.cgColor, UIColor.systemPink.cgColor]
        gradient.locations = [0, 1]
        gradient.frame = bounds
        layer.addSublayer(gradient)
    }

}

Comments

1

This does work for my project

/**
 set a gradient to a view
 gradientColors must be cgColors
 gradientColors and locations must have the same array size
 example:
 let gradientColors = [UIColor.red.cgColor,UIColor.blue.cgColor,UIColor.yellow.cgColor]
 let locations:[NSNumber] = [0.0,0.8,1.0]
 */
static func setGradient(viewWithGradient: UIView, backgroundColor: UIColor, gradientColors: [CGColor], locations:[NSNumber], boundsOfGradient:CGRect) {

    if gradientColors.count != locations.count {
        print("gradientColors and locations must have same size!")
        return
    }

    viewWithGradient.backgroundColor = backgroundColor
    let mask = CAGradientLayer()
    mask.colors = gradientColors
    mask.locations = locations
    mask.frame = boundsOfGradient
    viewWithGradient.layer.mask = mask
}

Call like this

    let gradientColors = [UIColor.red.cgColor,UIColor.blue.cgColor,UIColor.yellow.cgColor]
    let locations:[NSNumber] = [0.0,0.8,1.0]
    GeneralTools.setGradient(viewWithGradient: yourViewThatShouldGetGradient, backgroundColor: backgroundColorOfYourViewWithGradient, gradientColors: gradientColors, locations: locations, boundsOfGradient: viewWhereIsYourGradientInside.bounds)

Maybe not clear enough boundsOfGradient: The bounds will be set to your gradients frame. So simply said this will declare the size of your gradient.

Comments

1

gmoraleda's answer is correct. Do this on viewDidLayoutSubviews if you're using AutoLayout.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.