0

How can I set the background of a UIButton to a CAGradientLayer?

I have tried the following:

func applyGradient(colors: [UIColor], locations: [NSNumber]?) -> Void {
    let gradient: CAGradientLayer = CAGradientLayer()
    gradient.frame = self.bounds
    gradient.colors = colors.map { $0.cgColor }
    gradient.locations = locations
    self.layer.insertSublayer(gradient, at: 0)
}

which I got from Set Background Gradient on Button in Swift

and then to use it:

let startButton: UIButton = {
    let button = UIButton(type: .system)
    button.setTitle("Get Started", for: .normal)
    button.setTitleColor(AppSettings.PURPL_COLOR, for: .normal)
    button.titleLabel?.font = UIFont(name: "Rubik-Medium", size: 16.0)
    return button
}()

startButton.applyGradient(colors: [.red, .blue], locations: nil)

Also, I am setting the width of the button to the view's width and the height to 50.

However, when I run it, this is what I get:

result

The background gradient is not applied.

Omar Sinan
  • 117
  • 2
  • 10

1 Answers1

1

Bounds of the button will be 0 when you call the method applyGradient. So create a UIButton subclass and change the gradient layer frame in layoutSublayers method

class GradientButton: UIButton {
    let gradient: CAGradientLayer = CAGradientLayer()
    internal override init(frame: CGRect) {
        super.init(frame: frame)
    }
    internal required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    required init(_ colors: [UIColor], locations: [NSNumber]?) {
        super.init(frame: .zero)
        applyGradient(colors,locations:locations)
    }
    func applyGradient(_ colors: [UIColor], locations: [NSNumber]?) {
        gradient.colors = colors
        gradient.locations = locations
        gradient.startPoint = CGPoint(x: 0.0, y: 1.0)
        gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
        layer.insertSublayer(gradient, at: 0)
    }
    override func layoutSublayers(of layer: CALayer) {
        super.layoutSublayers(of: layer)
        gradient.frame = self.bounds
    }
}

And create the button with color and location values

let button = GradientButton([.red,.blue], locations: nil)
RajeshKumar R
  • 15,445
  • 2
  • 38
  • 70
  • Thanks for this, I'd rather use UIButton only. Would applying the gradient in viewDidLayoutSubviews be fine? – Omar Sinan May 28 '19 at 08:48
  • 1
    @OmarHashim Yes. Call `startButton.applyGradient(colors: [.red, .blue], locations: nil)` in `viewDidLayoutSubviews` – RajeshKumar R May 28 '19 at 08:49
  • @OmarSinan But `viewDidLayoutSubviews` method is called multiple times in a view controller. Every time you call `applyGradient` method it adds a new layer. Consider using `UIButton` subclass or keep a reference to the `gradientLayer` – RajeshKumar R May 28 '19 at 15:56
  • Oh right I did not think of that. Thanks for the heads up! – Omar Sinan May 28 '19 at 18:58