Search code examples
swiftcalayer

Setting corner radius as a negative value to achieve concave corners


I use this code to round my corners of UIView:

extension UIView {

    func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
        let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        self.layer.mask = mask
        self.layer.masksToBounds = false
    }
}

then I used view.roundCorners([.topLeft, .topRight], radius: view.frame.width/3)

Now this rounds the corners to be convex, and I want the opposite. Meaning the top corners will stay in the same points, but the line between them is rounded towards the inside of the view. I tried achieving this by calling the above function with a negative value, but this resulted with just nothing happening.

I added a picture that calrifies my goal. How could I achieve this? enter image description here


Solution

  • You'll need to create your own UIBezierPath. For the concave ends, addQuadCurve(to:controlPoint:) comes in handy.

    Here I'm using the parameter depth to control the amount of curve:

    extension UIView {
    
        func concaveEnds(depth: CGFloat) {
            let width = self.bounds.width
            let height = self.bounds.height
    
            let path = UIBezierPath()
            path.move(to: CGPoint.zero)
            path.addLine(to: CGPoint(x: width, y: 0))
            path.addQuadCurve(to: CGPoint(x: width, y: height), controlPoint: CGPoint(x: width - height * depth, y: height / 2))
            path.addLine(to: CGPoint(x: 0, y: height))
            path.addQuadCurve(to: CGPoint.zero, controlPoint: CGPoint(x: height * depth, y: height / 2))
            let mask = CAShapeLayer()
            mask.path = path.cgPath
            self.layer.mask = mask
            self.layer.masksToBounds = false
        }
    }
    

    Demo in a Playground:

    let view = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 100))
    view.backgroundColor = .red
    
    view.concaveEnds(depth: 0.4)
    

    Demo in Playground Demo with less curve