0

I made a custom gradient Button, such that I can change the gradient color on different states:

class GradientButton: UIButton {

public var buttongradient: CAGradientLayer = CAGradientLayer()

override var isSelected: Bool {
    didSet {
        if isSelected {
            buttongradient.colors = [UIColor(hexString: "#ef473a")!, UIColor(hexString: "#cb2d3e")!].map { $0.cgColor }
        } else {
            buttongradient.colors = [UIColor(hexString: "#29a1e2")! , UIColor(hexString: "#485ac8")!].map { $0.cgColor }
        }
    }
}

override init(frame: CGRect) {
    super.init(frame: frame)
    self.addthegradientLayer()
}

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

override func layoutSubviews() {
    super.layoutSubviews()
    self.addthegradientLayer()

}

func addthegradientLayer() {
    //the cells gradient colors
    buttongradient.frame = self.bounds
    buttongradient.cornerRadius = buttongradient.frame.height / 2
    buttongradient.colors = [UIColor(hexString: "#29a1e2")! , UIColor(hexString: "#485ac8")!].map { $0.cgColor }
    buttongradient.startPoint = CGPoint(x: 1.0, y: 0.0)
    buttongradient.endPoint = CGPoint(x: 0.0, y: 1.0)
    self.layer.insertSublayer(buttongradient, at: 0)
}

When I run this code, the button background is clear. No Gradient showing.

EDIT

As in the comments suggested:

buttongradient.frame = self.bounds

is the solution.

Follow up Problem

Now I realise, that the gradient does not change colors based on the isSelected state.

Mike
  • 53
  • 10
  • `buttongradient.frame = self.frame`=> `buttongradient.frame = self.bounds` seems better. – Larme Oct 25 '17 at 11:42
  • That's the solution! – Mike Oct 25 '17 at 11:45
  • See https://stackoverflow.com/questions/18955210/frame-and-bounds-of-cashapelayer https://stackoverflow.com/questions/11595555/calayer-as-sublayer-not-visible etc. – Larme Oct 25 '17 at 11:49
  • Possible duplicate of [Frame and Bounds of CAShapeLayer](https://stackoverflow.com/questions/18955210/frame-and-bounds-of-cashapelayer) – Larme Oct 25 '17 at 11:49
  • You should update the gradient only if the bounds really change otherwise you will have a huge performance problem. I know because I was fixing a similar one recently. You also probably want to use `isHighlighted` and not `isSelected`. – Sulthan Oct 25 '17 at 11:54

1 Answers1

1

There are multiple problems:

  1. As you already now, you have to use self.bounds to update the layer frame.
  2. Changing the layer in initializers is not necessary. The layoutSubviews is always called before rendering.
  3. If your aim is to change colors when the button is pressed, you want to use isHighlighted, not isSelected.
  4. Every layout will rewrite your color values, remove buttongradient.colors from the layout method.
  5. There is no need to use strings for hexadecimal color definition. I would recommend to use integers, e.g. 0x29a1e2 (see How to use hex colour values).
class GradientButton: UIButton {

    public let buttongradient: CAGradientLayer = CAGradientLayer()

    override var isSelected: Bool {  // or isHighlighted?
        didSet {
            updateGradientColors()
        }
    }

    func updateGradientColors() {
        let colors: [UIColor]

        if isSelected {
           colors = [UIColor(hexString: "#ef473a")!, UIColor(hexString: "#cb2d3e")!]
        } else {
           colors = [UIColor(hexString: "#29a1e2")!, UIColor(hexString: "#485ac8")!]
        }

        buttongradient.colors = colors.map { $0.cgColor }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setupGradient()
    }

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

    override func layoutSubviews() {
        super.layoutSubviews()
        self.updateGradient()
    }

    func setupGradient() {
        buttongradient.startPoint = CGPoint(x: 1.0, y: 0.0)
        buttongradient.endPoint = CGPoint(x: 0.0, y: 1.0)
        self.layer.insertSublayer(buttongradient, at: 0)

        updateGradientColors()
    }

    func updateGradient() {
        buttongradient.frame = self.bounds
        buttongradient.cornerRadius = buttongradient.frame.height / 2
    }
}
Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • Being new to Swift, what is the best way of calling this code from a UIButton? Is it by calling the updateGradientColors() function? I want to be able to change the colour of the UIButton when pressing it, moving onto the next UIView and coming back to the original UIView and the colour of the button is in the New Colour. – David_2877 Jun 04 '20 at 23:29