I have a UIView
that contains a 2 UILabels
with a button inside and I would like to add a gradient color to its boarder. I have managed to add it and button has ended up moving outside the custom UIView
with the custom UIView
also shrinking all the way outside on smaller devices. How can I fix the UIView
to remain the same size when I add a gradient color
Here is the initial UIView
with two UILabels
and a button inside with a normal border colour before
And here how it looks after applying a gradient color to it
Here is my code on how I apply the gradient.
@IBOutlet weak var customView: UIView!
let gradient = CAGradientLayer()
gradient.frame.size = self.customView.frame.size
gradient.colors = [UIColor.green.cgColor, UIColor.yellow.cgColor, UIColor.red.cgColor]
gradient.startPoint = CGPoint(x: 0.1, y: 0.78)
gradient.endPoint = CGPoint(x: 1.0, y: 0.78)
let shapeLayer = CAShapeLayer()
shapeLayer.path = UIBezierPath(rect: self.customView.bounds).cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.lineWidth = 4
gradient.mask = shapeLayer
self.customView.layer.addSublayer(gradient)
Layers do not resize when the view resizes, so you want to create a custom view and override layoutSubviews()
.
Here's an example:
@IBDesignable
class GradBorderView: UIView {
var gradient = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
layer.addSublayer(gradient)
}
override func layoutSubviews() {
gradient.frame = bounds
gradient.colors = [UIColor.green.cgColor, UIColor.yellow.cgColor, UIColor.red.cgColor]
gradient.startPoint = CGPoint(x: 0.1, y: 0.78)
gradient.endPoint = CGPoint(x: 1.0, y: 0.78)
let shapeLayer = CAShapeLayer()
shapeLayer.path = UIBezierPath(rect: bounds).cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.lineWidth = 4
gradient.mask = shapeLayer
}
}
Now, when your view changes size based on constraints and auto-layout, your gradient border will "auto-resize" correctly.
Also, by using @IBDesignable
, you can see the results when laying out your views in Storyboard / Interface Builder.
Here's how it looks with the Grad Border View
width set to 240
:
and with the Grad Border View
width set to 320
:
Edit
If we want to use rounded corners, we can set the shape layer path to a rounded rect bezier path, and then also set the corner radius of the view's layer.
For example:
override func layoutSubviews() {
let cRadius: CGFloat = 8.0
let bWidth: CGFloat = 4.0
// layer border is centered on layer edge
let half: CGFloat = bWidth * 0.5
// make gradient frame size of view + half the border width
gradient.frame = bounds.insetBy(dx: -half, dy: -half)
gradient.colors = [UIColor.green.cgColor, UIColor.yellow.cgColor, UIColor.red.cgColor]
gradient.startPoint = CGPoint(x: 0.1, y: 0.78)
gradient.endPoint = CGPoint(x: 1.0, y: 0.78)
let shapeLayer = CAShapeLayer()
// make shapeLayer path the size of view OFFSET by half the border width
// with rounded corners
shapeLayer.path = UIBezierPath(roundedRect: bounds.offsetBy(dx: half, dy: half), cornerRadius: cRadius).cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.lineWidth = bWidth
gradient.mask = shapeLayer
// same corner radius as shapeLayer path
layer.cornerRadius = cRadius
}