SnapKit and custom gradient button Swift

485 Views Asked by At

I use SnapKit in my project and trying to add gradient on my button

I have extension:

extension UIButton {
    
    public func setGradientColor(colorOne: UIColor, colorTwo: UIColor) {
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = bounds
        gradientLayer.colors = [colorOne.cgColor, colorTwo.cgColor]
        gradientLayer.locations = [0.0, 1.0]
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
        gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
        
        layer.insertSublayer(gradientLayer, at: 0)
    }
}

i have button config where i am trying to use gradient:

private var playersButton: UIButton = {
        let button = UIButton(type: .custom)
        button.setGradientColor(colorOne: .red, colorTwo: .blue)
        button.frame = button.layer.frame
  
        return button
    }() 

and SnapKit here

playersButton.snp.makeConstraints { make in
            make.leading.equalToSuperview().inset(60)
            make.trailing.equalToSuperview().inset(60)
            make.bottom.equalTo(startGameButton).inset(100)
            make.height.equalTo(screenHeight/12.82)
        }

The problem is i have not result of it, i dont see gradientm but if i delete snapKit config and will use setGradient extension in viewDidLoad it works well!

Example:

  private var playersButton: UIButton = {
        let button = UIButton(type: .custom)
        button.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
    
        return button
    }()
override func viewDidLoad() {
        super.viewDidLoad()
        playersButton.setGradientColor(colorOne: .blue, colorTwo: .red)
    }

How do i change my code to use first method? Thank you

1

There are 1 best solutions below

1
On

The difference is that when using SnapKit you're using Auto-Layout, so your button's frame is not set when you call button.setGradientColor(colorOne: .red, colorTwo: .blue).

The result is that your gradient layer ends up with a frame size of Zero - so you don't see it.

You will likely find it much easier and more reliable to use a button subclass like this (simple example based on the code you posted):

class MyGradientButton: UIButton {
    let gradLayer = CAGradientLayer()
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        layer.insertSublayer(gradLayer, at: 0)
    }
    public func setGradientColor(colorOne: UIColor, colorTwo: UIColor) {
        gradLayer.colors = [colorOne.cgColor, colorTwo.cgColor]
        gradLayer.locations = [0.0, 1.0]
        gradLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
        gradLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        gradLayer.frame = bounds
    }
}

Then change your button declaration to:

private var playersButton: MyGradientButton = {
    let button = MyGradientButton()
    button.setGradientColor(colorOne: .red, colorTwo: .blue)
    return button
}()

Now, whether you set its frame explicitly, or if you use auto-layout (normal syntax or with SnapKit), the gradient layer will automatically adjust whenever the button's frame changes.