3

I want to set border color with the gradient left to right [Red, Green] at UIView. Like example:

enter image description here

I tried below code: -

class View: UIView {

    override func layoutSubviews() {
        super.layoutSubviews()

        let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: [.topLeft, .bottomLeft, .topRight, .bottomRight], cornerRadii: CGSize(width: frame.size.height / 2, height: frame.size.height / 2))

        let gradient = CAGradientLayer()
        gradient.frame =  CGRect(origin: CGPoint.zero, size: frame.size)
        gradient.colors = [UIColor.green.cgColor, UIColor.red.cgColor]

        let shape = CAShapeLayer()
        shape.lineWidth = 10
        shape.path = path.cgPath
        shape.strokeColor = UIColor.black.cgColor
        shape.fillColor = UIColor.clear.cgColor
        gradient.mask = shape

        layer.insertSublayer(gradient, at: 0)
    }
}

There is three problem which I am not able to resolve: -
1- I have set lineWidth 10 but its showing width 10 at corners and at horizontal/vertical only 5.
2- I want to show the gradient from left to right not top to bottom.

I tried below code to set gradient from left to right but not working: -

//        gradient.frame =  CGRect(origin: CGPoint.zero, size: frame.size)
        gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
        gradient.endPoint = CGPoint(x: 1.0, y: 0.5)

enter image description here

Please help. Thanks in advance.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Asif Raza
  • 836
  • 12
  • 29

2 Answers2

4

Edit

I want to set border from left to right

You need to change gradient.startPoint and gradient.endPoint

enum Direction {
    case horizontal
    case vertical
}

class View: UIView {

init(frame: CGRect, cornerRadius: CGFloat, colors: [UIColor], lineWidth: CGFloat = 5, direction: Direction = .horizontal) {
    super.init(frame: frame)

    self.layer.cornerRadius = cornerRadius
    self.layer.masksToBounds = true
    let gradient = CAGradientLayer()
    gradient.frame = CGRect(origin: CGPoint.zero, size: self.frame.size)
    gradient.colors = colors.map({ (color) -> CGColor in
        color.cgColor
    })

    switch direction {
    case .horizontal:
        gradient.startPoint = CGPoint(x: 0, y: 1)
        gradient.endPoint = CGPoint(x: 1, y: 1)
    case .vertical:
        gradient.startPoint = CGPoint(x: 0, y: 0)
        gradient.endPoint = CGPoint(x: 0, y: 1)
    }

    let shape = CAShapeLayer()
    shape.lineWidth = lineWidth
    shape.path = UIBezierPath(roundedRect: self.bounds.insetBy(dx: lineWidth, 
    dy: lineWidth), cornerRadius: cornerRadius).cgPath
    shape.strokeColor = UIColor.black.cgColor
    shape.fillColor = UIColor.clear.cgColor
    gradient.mask = shape

    self.layer.addSublayer(gradient)
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

As you can see, I added some extra parameters. I solved this issue by adding inset to the roundedRect:

shape.path = UIBezierPath(roundedRect: self.bounds.insetBy(dx: lineWidth, 
dy: lineWidth), cornerRadius: cornerRadius).cgPath

How to use it:

let myView = View(frame: CGRect(x: 0, y: 0, width: 200, height: 50), cornerRadius: 25, colors: [UIColor.red, .orange, .yellow], lineWidth: 2, direction: .horizontal)
    myView.center = view.center
    view.addSubview(myView)

Screenshot:

roundedViewWithGradient

mrcfal
  • 424
  • 3
  • 9
  • Thanks @rmaddy, but how can I render gradient from left to right? I want to draw the gradient from left to right. Like https://i.stack.imgur.com/V01uy.png – Asif Raza Jul 14 '18 at 06:14
  • I edited my answer so you can choose if you want vertical gradient or horizontal one. – mrcfal Jul 14 '18 at 07:33
2

The problem is that the the bezier path is not drawn inside its bounds. It's drawn around its bounds. So half the stroke width is inside and half is outside. You need to adjust the bounds to deal with this.

Change the frame you pass into the bezier path from self.bounds to self.bounds.insetBy(dx: 5, dy: 5) where 5 is half your line width.

And the lines:

gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)

do result in the gradient going from left to right.

Here is fully working code from yours:

class View: UIView {
    override func layoutSubviews() {
        super.layoutSubviews()

        let path = UIBezierPath(roundedRect: self.bounds.insetBy(dx: 5, dy: 5), byRoundingCorners: [.topLeft, .bottomLeft, .topRight, .bottomRight], cornerRadii: CGSize(width: frame.size.height / 2, height: frame.size.height / 2))

        let gradient = CAGradientLayer()
        gradient.frame =  CGRect(origin: CGPoint.zero, size: frame.size)
        gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
        gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
        gradient.colors = [UIColor.green.cgColor, UIColor.red.cgColor]

        let shape = CAShapeLayer()
        shape.lineWidth = 10
        shape.path = path.cgPath
        shape.strokeColor = UIColor.black.cgColor
        shape.fillColor = UIColor.clear.cgColor
        gradient.mask = shape

        layer.insertSublayer(gradient, at: 0)
    }
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Thanks @rmaddy, but how can I render gradient from left to right? – Asif Raza Jul 14 '18 at 06:13
  • The code in my answer does render the gradient from left to right. When this class is copied, as-is, into a Swift playground and then an instance of this `View` class is created, the resulting view has the oval with the green on the left and the red on the right. – rmaddy Jul 14 '18 at 12:14