I have looked over a bunch of other answers to questions like this, but none of the answers seem to solve my problem, so looking for a little help.
I'm trying to apply a vertical gradient to a UIButton, but the gradient layer is just not showing up in my view. Here is the relevant code:
let darkPurple = UIColor(displayP3Red: 61.0/255.0, green: 3.0/255.0, blue: 110.0/255.0, alpha: 1.0)
let lightPurple = UIColor(displayP3Red: 90.0/255.0, green: 32.0/255.0, blue: 130.0/255.0, alpha: 1.0)
let addButton = UIButton(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
addButton.title = "Start counting"
addButton.layer.cornerRadius = 30.0
addButton.layer.borderWidth = 1.0
addButton.layer.borderColor = darkPurple.cgColor
addButton.translatesAutoresizingMaskIntoConstraints = false
myView.addSubview(addButton)
addButton.applyGradient(with: [lightPurple, darkPurple], gradientOrientation: .vertical)
...and the code to apply the gradient:
typealias GradientPoints = (startPoint: CGPoint, endPoint: CGPoint)
enum GradientOrientation {
case topRightBottomLeft
case topLeftBottomRight
case horizontal
case vertical
var startPoint: CGPoint {
return points.startPoint
}
var endPoint: CGPoint {
return points.endPoint
}
var points: GradientPoints {
switch self {
case .topRightBottomLeft:
return (CGPoint(x: 0.0, y: 1.0), CGPoint(x: 1.0, y: 0.0))
case .topLeftBottomRight:
return (CGPoint(x: 0.0, y: 0.0), CGPoint(x: 1.0, y: 1.0))
case .horizontal:
return (CGPoint(x: 0.0, y: 0.0), CGPoint(x: 1.0, y: 0.0))
case .vertical:
return (CGPoint(x: 0.0, y: 0.0), CGPoint(x: 0.0, y: 1.0))
}
}
}
extension UIView {
func applyGradient(with colors: [UIColor], gradientOrientation orientation: GradientOrientation) {
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = colors.map { $0.cgColor }
gradient.startPoint = orientation.startPoint
gradient.endPoint = orientation.endPoint
gradient.borderColor = self.layer.borderColor
gradient.borderWidth = self.layer.borderWidth
gradient.cornerRadius = self.layer.cornerRadius
gradient.masksToBounds = true
gradient.isHidden = false
self.layer.insertSublayer(gradient, at: 0)
}
}
This is what I get: empty button
Thanks for your help!
Auto Layout doesn't affect the layers of UIView
objects, so the gradient layer is being applied too early, before the frame of the UIButton
has been calculated. When the frame
property of the gradient layer is assigned, the bounds of the button are still equal to CGRect.zero
, and the gradient layer frame is never updated later.
You'll find that if you add your gradient layer after the button frame has been calculated, it works as expected. For example:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let darkPurple = UIColor(displayP3Red: 61.0/255.0, green: 3.0/255.0, blue: 110.0/255.0, alpha: 1.0)
let lightPurple = UIColor(displayP3Red: 90.0/255.0, green: 32.0/255.0, blue: 130.0/255.0, alpha: 1.0)
addButton.applyGradient(with: [lightPurple, darkPurple], gradientOrientation: .vertical)
}
Another option would be to update the frame
property of the gradient layer in viewDidLayoutSubviews()
or create a custom UIButton
subclass and update the gradient layer frame whenever the frame of the button updates.