iosswiftgradientcalayercagradientlayer

Set gradient color on custom UIView boarder


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

enter image description here

And here how it looks after applying a gradient color to it

enter image description here

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)

Solution

  • 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:

    enter image description here

    and with the Grad Border View width set to 320:

    enter image description here


    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
    }