skip to Main Content

How would I draw an inverted rounded rectangle as shown below in Swift using a UIBezierPath? To clarify, I want to ONLY draw the shape in black.

Inverted Rounded Rectangle

2

Answers


  1. What I do is draw two views: the black rectangle, and the top region view with the curves (which I call a "roundyThingy"). So the only interesting part is how to draw the top region view. It too is just a black rectangle, but it has a mask that cuts out the roundy part. So the only really interesting part is the mask:

        let size = roundyThingy.bounds.size
        let renderer = UIGraphicsImageRenderer(size: size)
        let image = renderer.image { context in
            UIColor.black.setFill()
            context.fill(.init(origin: .zero, size: size))
            let path = UIBezierPath(
                roundedRect: .init(origin: .zero, size: size),
                byRoundingCorners: [.bottomLeft, .bottomRight],
                cornerRadii: .init(width: 12, height: 12)
            )
            path.fill(with: .clear, alpha: 1)
        }
        roundyThingy.mask = UIImageView(image: image)
        roundyThingy.mask?.frame = roundyThingy.bounds
    
    Login or Signup to reply.
  2. While most of us would simply put a white view with rounded corners on top of the black view (it is simpler and it more accurately reflects the visual result), if you really want to draw that shape, create a UIBezierPath that consists of the upper left arc, the upper right arc, and the add lines to the two bottom corners. E.g.

    let path = UIBezierPath(
        arcCenter: CGPoint(x: rect.minX + cornerRadius, y: rect.minY),
        radius: cornerRadius,
        startAngle: .pi,
        endAngle: .pi / 2,
        clockwise: false
    )
    path.addArc(
        withCenter: CGPoint(x: rect.maxX - cornerRadius, y: rect.minY),
        radius: cornerRadius,
        startAngle: .pi / 2,
        endAngle: 0,
        clockwise: false
    )
    path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
    path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
    path.close()
    

    There are a ton of ways to render this:

    • Create CAShapeLayer with this path and use that as a mask (which I show below);
    • Add this CAShapeLayer as a sublayer of the view’s layer;
    • Create a UIImage using UIGraphicsImageRenderer, and fill that UIBezierPath;
    • etc.

    For example:

    @IBDesignable class BottomView: UIView {
        @IBInspectable var cornerRadius: CGFloat = 15 { didSet { setNeedsLayout() } }
    
        override func layoutSubviews() {
            super.layoutSubviews()
    
            let path = UIBezierPath(
                arcCenter: CGPoint(x: bounds.minX + cornerRadius, y: bounds.minY),
                radius: cornerRadius,
                startAngle: .pi,
                endAngle: .pi / 2,
                clockwise: false
            )
            path.addArc(
                withCenter: CGPoint(x: bounds.maxX - cornerRadius, y: bounds.minY),
                radius: cornerRadius,
                startAngle: .pi / 2,
                endAngle: 0,
                clockwise: false
            )
            path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
            path.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
            path.close()
    
            let shapeLayer = CAShapeLayer()
            shapeLayer.path = path.cgPath
            shapeLayer.fillColor = UIColor.white.cgColor // the color here is irrelevant; only used to define the mask
    
            layer.mask = shapeLayer
        }
    }
    

    That yields the following when I (a) add this BottomView to my view hierarchy; and (b) set the backgroundColor of this view to .black (or whatever you want):

    enter image description here

    I made this @IBDesignable so that I could either add it in Interface Builder storyboard, but you can also add it programmatically, too.

    Or you can use this UIBezierPath with any of the patterns enumerated above. Whatever works for you.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search