4

I have a CGRect value and need to draw a border around it. I just want the corners to be drawn without any lines connecting them.

Something like this...

enter image description here

How would I draw this figure using swift?

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
coder
  • 381
  • 2
  • 22
  • Have you tried using a UIBezierPath to draw one corner? – Abizern Mar 03 '18 at 22:32
  • No, I have only used UIBezierPath a couple of times before. I only have drawn a complete rectangle with UIBezierPath. @Abizern – coder Mar 03 '18 at 22:34
  • 1
    You cannot draw a border around CGRect. Because it just contains information about rect and it is not visible. as a result, what do you want to receive? CALayer, UIView? – O.Daniel Mar 03 '18 at 22:59
  • I would draw the rounded rect and erase the middles of the lines. – matt Mar 03 '18 at 23:05
  • I was looking to draw the shape with CAShapleLayer or something like that. I don’t want an image. – coder Mar 03 '18 at 23:16
  • Could also do: https://pastebin.com/mSehYjr8 if you want just simple corners drawn. – Brandon Mar 04 '18 at 00:13

3 Answers3

14

Here's a custom UIView class that draws itself with the four corners. You can set various properties to get the look you need.

class CornerRect: UIView {
    var color = UIColor.black {
        didSet {
            setNeedsDisplay()
        }
    }
    var radius: CGFloat = 5 {
        didSet {
            setNeedsDisplay()
        }
    }
    var thickness: CGFloat = 2 {
        didSet {
            setNeedsDisplay()
        }
    }
    var length: CGFloat = 30 {
        didSet {
            setNeedsDisplay()
        }
    }

    override func draw(_ rect: CGRect) {
        color.set()

        let t2 = thickness / 2
        let path = UIBezierPath()
        // Top left
        path.move(to: CGPoint(x: t2, y: length + radius + t2))
        path.addLine(to: CGPoint(x: t2, y: radius + t2))
        path.addArc(withCenter: CGPoint(x: radius + t2, y: radius + t2), radius: radius, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 3 / 2, clockwise: true)
        path.addLine(to: CGPoint(x: length + radius + t2, y: t2))

        // Top right
        path.move(to: CGPoint(x: frame.width - t2, y: length + radius + t2))
        path.addLine(to: CGPoint(x: frame.width - t2, y: radius + t2))
        path.addArc(withCenter: CGPoint(x: frame.width - radius - t2, y: radius + t2), radius: radius, startAngle: 0, endAngle: CGFloat.pi * 3 / 2, clockwise: false)
        path.addLine(to: CGPoint(x: frame.width - length - radius - t2, y: t2))

        // Bottom left
        path.move(to: CGPoint(x: t2, y: frame.height - length - radius - t2))
        path.addLine(to: CGPoint(x: t2, y: frame.height - radius - t2))
        path.addArc(withCenter: CGPoint(x: radius + t2, y: frame.height - radius - t2), radius: radius, startAngle: CGFloat.pi, endAngle: CGFloat.pi / 2, clockwise: false)
        path.addLine(to: CGPoint(x: length + radius + t2, y: frame.height - t2))

        // Bottom right
        path.move(to: CGPoint(x: frame.width - t2, y: frame.height - length - radius - t2))
        path.addLine(to: CGPoint(x: frame.width - t2, y: frame.height - radius - t2))
        path.addArc(withCenter: CGPoint(x: frame.width - radius - t2, y: frame.height - radius - t2), radius: radius, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: true)
        path.addLine(to: CGPoint(x: frame.width - length - radius - t2, y: frame.height - t2))

        path.lineWidth = thickness
        path.stroke()
    }
}

Sample usage:

let cr = CornerRect(frame: CGRect(x: 0, y: 0, width: 300, height: 500))
cr.color = .yellow
cr.thickness = 5
cr.backgroundColor = .white

Copy and paste that into a playground. Try different values for the properties.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
4

You draw those shapes the same way Superman gets into his tights: one leg at a time. Divide the shape into three parts: the vertical leg, the 90-degree arc that forms the rounded corner, and the horizontal leg. Now just draw each of those in turn.

I'll illustrate by showing how to draw one corner; the other three corners are similar and symmetrical and are left as an exercise for the reader.

Assume r is our rect and that we are in a drawing context (e.g. a UIView's draw(_:) or an image view graphics context). Let's make some initial assumptions (feel free to change them):

UIColor.yellow.setStroke()
let segLength : CGFloat = 40
let cornerSize : CGFloat = 10
let lineWidth : CGFloat = 6

Now we just form a bezier path describing one leg of the corner, the rounded corner arc, and the other leg of the corner, and stroke it:

 let p = UIBezierPath()
 p.lineWidth = lineWidth
 // draw top left corner
 p.move(to: CGPoint(x:r.minX, y:r.minY + segLength + cornerSize))
 p.addLine(to: CGPoint(x:r.minX, y:r.minY + cornerSize))
 p.addArc(withCenter: CGPoint(x:r.minX + cornerSize, y:r.minY + cornerSize),
    radius: cornerSize,
    startAngle: CGFloat.pi,
    endAngle: CGFloat.pi * 3.0 / 2.0,
    clockwise: true)
p.addLine(to:CGPoint(x:r.minX + segLength + cornerSize, y:r.minY))
p.stroke()

The result looks like this (I've shaded the background in blue to make it easier to see, and I've zoomed in a bit):

enter image description here

The other three corners work exactly the same way, just changing the obvious things that would need to be changed.

matt
  • 515,959
  • 87
  • 875
  • 1,141
1

since it took me a while to figure it out, I'm going to share the "obvious things that would need to be changed" to complete the rest of the rectangle using @matt's answer :)

let p = UIBezierPath()
p.lineWidth = lineWidth
// draw top left corner
p.move(to: CGPoint(x:r.minX, y:r.minY + segLength + cornerSize))
p.addLine(to: CGPoint(x:r.minX, y:r.minY + cornerSize))
p.addArc(withCenter: CGPoint(x:r.minX + cornerSize, y:r.minY + cornerSize),
            radius: cornerSize, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 1.5, clockwise: true)
p.addLine(to:CGPoint(x:r.minX + segLength + cornerSize, y:r.minY))
// draw top right corner
p.move(to: CGPoint(x:r.maxX - segLength - cornerSize, y:r.minY ))
p.addLine(to: CGPoint(x:r.maxX - cornerSize, y:r.minY ))
p.addArc(withCenter: CGPoint(x:r.maxX - cornerSize, y:r.minY + cornerSize),
            radius: cornerSize, startAngle: CGFloat.pi * 1.5, endAngle: 0, clockwise: true)
p.addLine(to:CGPoint(x:r.maxX, y:r.minY + segLength + cornerSize))
// draw bottom right corner
p.move(to: CGPoint(x:r.maxX, y:r.maxY - segLength - cornerSize))
p.addLine(to: CGPoint(x:r.maxX, y:r.maxY - cornerSize ))
p.addArc(withCenter: CGPoint(x:r.maxX - cornerSize, y:r.maxY - cornerSize),
radius: cornerSize, startAngle: 0, endAngle: CGFloat.pi * 0.5, clockwise: true)
p.addLine(to:CGPoint(x:r.maxX - segLength - cornerSize, y:r.maxY))
// draw bottom left corner
p.move(to: CGPoint(x:r.minX + segLength + cornerSize, y:r.maxY))
p.addLine(to: CGPoint(x:r.minX + cornerSize, y:r.maxY ))
p.addArc(withCenter: CGPoint(x:r.minX + cornerSize, y:r.maxY - cornerSize),
radius: cornerSize, startAngle: CGFloat.pi * 0.5, endAngle: CGFloat.pi, clockwise: true)
p.addLine(to:CGPoint(x:r.minX, y:r.maxY - segLength - cornerSize))
p.stroke()
themenace
  • 2,601
  • 2
  • 20
  • 33