I have custom UIView
class that renders a gradient in Swift 2. I'm struggling with making an angled gradient so that it draws from the top-left to the bottom-right. Can somebody help me a bit?
import UIKit
class GradientView: UIView {
let gradientLayer = CAGradientLayer()
override func awakeFromNib() {
// 1
self.backgroundColor = ColorPalette.White
// 2
gradientLayer.frame = self.bounds
// 3
let color1 = ColorPalette.GrdTop.CGColor as CGColorRef
let color2 = ColorPalette.GrdBottom.CGColor as CGColorRef
gradientLayer.colors = [color1, color2]
// 4
gradientLayer.locations = [0.0, 1.0]
// 5
self.layer.addSublayer(gradientLayer)
}
}
I suspect this should be something else but whatever I input nothing changes.
gradientLayer.locations = [0.0, 1.0]
You don't want to use locations
to specify the direction of the gradient. Instead use startPoint
and endPoint
for that.
The locations
array is used when one wants to specify where, in between startPoint
and endPoint
, the gradient should to take place. For example, if you want the colors to only take place in the middle 10% of the range from the start and end points, you'd use:
locations = [0.45, 0.55]
The locations
array doesn't dictate the direction. The startPoint
and endPoint
do. So, for a diagonal gradient from upper left to lower right, you would set startPoint
of CGPoint(x: 0, y: 0)
and an endPoint
to CGPoint(x: 1, y: 1)
.
For example:
@IBDesignable
class GradientView: UIView {
override class var layerClass: AnyClass { return CAGradientLayer.self }
private var gradientLayer: CAGradientLayer { return layer as! CAGradientLayer }
@IBInspectable var color1: UIColor = .white { didSet { updateColors() } }
@IBInspectable var color2: UIColor = .blue { didSet { updateColors() } }
override init(frame: CGRect = .zero) {
super.init(frame: frame)
configureGradient()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configureGradient()
}
private func configureGradient() {
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1)
updateColors()
}
private func updateColors() {
gradientLayer.colors = [color1.cgColor, color2.cgColor]
}
}
E.g.
Note, unrelated to the immediate issue:
If you’re going to add the gradient as a sublayer, you want to update this sublayer’s frame
in layoutSubviews
so that as the view's bounds
changes, so does the frame
of the gradientLayer
. But, better than that, override the layerClass
of the view, and it will not only instantiate the CAGradientLayer
for you, but you also enjoy dynamic adjustments of the gradient as the view’s size changes, notably handling animated changes more gracefully.
Likewise, I set color1
and color2
such that they'll trigger an updating of the gradient, so that any changes in colors will be immediately reflected in the view.
I made this @IBDesignable
, so that if I drop this in its own framework, and then add the GradientView
in IB, I'll see the effect rendered in IB.
For Swift 2 implementation, see previous revision of this answer.