Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gradient Layer not showing on UIButton

I am trying to create a custom UIButton that i can use throughout my application. I would like to apply a gradient as its background however the gradient is not showing up using this very simple Code. It would be very helpful if someone could point out my mistake in the code below.

class GradientBtn: UIButton {

    let gradientLayer = CAGradientLayer()

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

        themeConfig()
    }

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

        themeConfig()
    }

    private func themeConfig() {
        //shadow
        layer.shadowOffset = CGSize.zero
        layer.shadowColor = UIColor.gray.cgColor
        layer.shadowOpacity = 1.0

        //titletext
        setTitleColor(Theme.colorWhite, for: .normal)
        titleLabel?.font = UIFont(name: Theme.fontAvenir, size: 18)

        //rounded corners
        layer.cornerRadius = frame.size.height / 2

        //gradient
        gradientLayer.locations = [0.0, 1.0]
        gradientLayer.colors = [Theme.colorlightBlue, Theme.colorMidBlue]
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
        gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
        gradientLayer.frame = bounds
        layer.insertSublayer(gradientLayer, at: 0)

    }

}
like image 638
Aman Chawla Avatar asked Dec 30 '25 04:12

Aman Chawla


2 Answers

Replace

gradientLayer.colors = [Theme.colorlightBlue, Theme.colorMidBlue]

with

gradientLayer.colors = [Theme.colorlightBlue.cgColor, Theme.colorMidBlue.cgColor]

And it's always good to add

override func layoutSubviews() {
    super.layoutSubviews()
    gradientLayer.frame = self.bounds
}
like image 67
Sh_Khan Avatar answered Dec 31 '25 17:12

Sh_Khan


Sh_Khan is right about the CGColor references, but a few additional refinements:

  1. Rather than adding the gradient as a sublayer, make it the layerClass of the UIButton. This makes animations much better (e.g. if the button changes size on device rotation).

    When you do this, remove all attempts to set the frame of the gradientLayer.

  2. In layoutSubviews, you’ll no longer need to update the gradientLayer.frame, so remove that entirely, but you do want to put your corner rounding there.

  3. It’s extremely unlikely to ever matter, but when you set layer.cornerRadius (or any property of the view, its layer, or that layer's sublayers), you should never refer to self.frame. The frame of the view is the CGRect in the superview’s coordinate system. Only refer to the view’s bounds within the subclass; never its frame.

Thus:

class GradientBtn: UIButton {
    
    override class var layerClass: AnyClass { return CAGradientLayer.self }
    private var gradientLayer: CAGradientLayer { return layer as! CAGradientLayer }
    
    override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        
        themeConfig()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        themeConfig()
    }
    
    private func themeConfig() {
        //shadow
        layer.shadowOffset = .zero
        layer.shadowColor = UIColor.gray.cgColor
        layer.shadowOpacity = 1
        
        //titletext
        setTitleColor(Theme.colorWhite, for: .normal)
        titleLabel?.font = UIFont(name: Theme.fontAvenir, size: 18)
        
        //gradient
        gradientLayer.locations = [0, 1]
        gradientLayer.colors = [Theme.colorlightBlue, Theme.colorMidBlue].map { $0.cgColor }
        gradientLayer.startPoint = CGPoint(x: 0, y: 0)
        gradientLayer.endPoint = CGPoint(x: 1, y: 1)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        layer.cornerRadius = min(bounds.height, bounds.width) / 2
    }
}
like image 28
Rob Avatar answered Dec 31 '25 18:12

Rob