0

I have UIView which I need to draw a border only partial part view, how do I do in using UIBezierPathenter image description here

Ram
  • 269
  • 2
  • 9
  • check this out https://stackoverflow.com/questions/36836367/how-can-i-do-programmatically-gradient-border-color-uibutton-with-swift – Hammer Class Aug 21 '20 at 12:50

2 Answers2

2

If you want to use UIBezierPath, you could choose the "silly" way:

  • stroke the full border of your view
  • "cover up" the part where you don't want the border to exist with the view's background color, by drawing a rectangle on top of the border.
class MyView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    private func commonInit() {
        layer.cornerRadius = 3
        backgroundColor = .white
        layer.masksToBounds = true
    }
    
    override func draw(_ rect: CGRect) {
        let borderPath = UIBezierPath(roundedRect: bounds, cornerRadius: 3)
        borderPath.lineWidth = 7
        UIColor.gray.setStroke()
        borderPath.stroke()
        let covering = UIBezierPath(rect: CGRect(x: 20, y: -10, width: self.bounds.width - 40, height: 20))
        backgroundColor?.setFill()
        covering.fill()
    }
}

Output:

enter image description here

Other than that, I can't think of an easy way to do this.

However, if you can use CAShapeLayer...

You should set the strokeEnd and strokeStart properties of a CAShapeLayer added as a sublayer of your view's layer.

Example:

class MyView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    private func commonInit() {
        layer.cornerRadius = 3
        let border = CAShapeLayer()
        // make sure this path coincides with the border of the view
        border.path = UIBezierPath(roundedRect: bounds, cornerRadius: 3).cgPath

        // you should tweak these numbers
        border.strokeStart = 0.3
        border.strokeEnd = 0.7

        border.strokeColor = UIColor.gray.cgColor
        border.lineWidth = 3
        border.fillColor = nil
        layer.addSublayer(border)
        backgroundColor = .white
    }
}

Output:

enter image description here

Sweeper
  • 213,210
  • 22
  • 193
  • 313
2

I did exactly the same thing but animated once, to mimic android materials textfield design and behavior in ios app here is the shape creation code:

placeholderWidth is size of gap on top, leftInset is inset from frame left corner

class FloatingTextfieldBorderShapeCreator {
    
    var leftInset: CGFloat

    init(leftInset: CGFloat) {
        self.leftInset = leftInset
    }

    func create(frame: CGRect, placeholderWidth: CGFloat) -> CGPath {
        let path = UIBezierPath()
        let arcRadius: CGFloat = 4.0
    
        //1. starting point top left
        let startingPoint = CGPoint(x: frame.minX + leftInset - 4, y: frame.minY)
        path.move(to: startingPoint)
    
        //2. top left arc
        let topLeftArcCenter = CGPoint(x: frame.minX + arcRadius, y: arcRadius)
        path.addArc(withCenter: topLeftArcCenter, radius: arcRadius, startAngle: 3*CGFloat.pi/2, endAngle: CGFloat.pi, clockwise: false)
    
        //3. left line
        let leftLineEndPoint = CGPoint(x: frame.minX, y: frame.height - arcRadius)
        path.addLine(to: leftLineEndPoint)
    
        //4. bottom left arc
        let bottomLeftArcCenter = CGPoint(x: frame.minX + arcRadius, y: frame.height - arcRadius)
        path.addArc(withCenter: bottomLeftArcCenter, radius: arcRadius, startAngle: CGFloat.pi, endAngle: CGFloat.pi/2, clockwise: false)
    
        //5. bottom line
        let bottomLineEndPoint = CGPoint(x: frame.width - arcRadius, y: frame.height)
        path.addLine(to: bottomLineEndPoint)
    
        //6. bottom right arc
        let bottomRightArcCenter = CGPoint(x: frame.width - arcRadius, y: frame.height - arcRadius)
        path.addArc(withCenter: bottomRightArcCenter, radius: arcRadius, startAngle: CGFloat.pi/2, endAngle: 0, clockwise: false)
    
        //7. right line
        let rightLineEndPoint = CGPoint(x: frame.width, y: frame.minY + arcRadius)
        path.addLine(to: rightLineEndPoint)
    
        //8. top right arc
        let topRightArcCenter = CGPoint(x: frame.width - arcRadius, y: frame.minY + arcRadius)
        path.addArc(withCenter: topRightArcCenter, radius: arcRadius, startAngle: 0, endAngle: -CGFloat.pi/2, clockwise: false)
    
        //9. top line
        let topLineEndPointX = startingPoint.x + placeholderWidth
        let topLineEndPoint = CGPoint(x: topLineEndPointX, y: frame.minY)
        path.addLine(to: topLineEndPoint)
    
        return path.cgPath
    }
}

and properties to apply on drawing layer:

    borderLayer.frame = borderFrame
    borderLayer.fillColor = nil
    borderLayer.lineWidth = 1.0
    borderLayer.strokeColor = .lightGray
Robert Juz
  • 126
  • 7