// Shape with indented Notched Tab View // based on: DuncanMC's https://github.com/DuncanMC/ShapeWithTab class ShapeWithNotchedTabView: UIView { var cornerRadius: CGFloat = 20 var tabRadius: CGFloat = 60 var roundedCorners: Bool = true { didSet { setNeedsLayout() } } var shapeLayer = CAShapeLayer() func buildShapeLayerPath() -> CGPath { let boxWidth = bounds.size.width let boxHeight = bounds.size.height // These are the corners of the view's primary rectangle // top-right let point1 = CGPoint(x: boxWidth, y: 0) // bottom-right let point2 = CGPoint(x: boxWidth, y: boxHeight) // bottom-left let point3 = CGPoint(x: 0, y: boxHeight) // top-left let point4 = CGPoint(x: 0, y: 0) // These are the corners of the "tab notch" that is inset from the top of the view let tabPoint1 = CGPoint(x: boxWidth / 2 - tabRadius, y: 0) let tabPoint2 = CGPoint(x: boxWidth / 2, y: tabRadius * 2) let tabPoint3 = CGPoint(x: boxWidth / 2 + tabRadius , y: 0) let path = CGMutablePath() // if we want rounded corners if roundedCorners { path.move(to: CGPoint(x: boxWidth, y: boxHeight - cornerRadius)) path.addArc(tangent1End: point2, tangent2End: point3, radius: cornerRadius) path.addArc(tangent1End: point3, tangent2End: point4, radius: cornerRadius) path.addArc(tangent1End: point4, tangent2End: tabPoint1, radius: cornerRadius) } else { path.move(to: point1) path.addLine(to: point2) path.addLine(to: point3) path.addLine(to: point4) } // Draw the corner that curves around to our // indented "circle tab notch" at the top // of the shape path.addArc(tangent1End: tabPoint1, tangent2End: tabPoint2, radius: cornerRadius) // Draw the "circle tab notch" path.addArc(tangent1End: tabPoint2, tangent2End: tabPoint3, radius: tabRadius) // Draw the corner from the circle tab notch back to the top of the rect path.addArc(tangent1End: tabPoint3, tangent2End: point1, radius: cornerRadius) if roundedCorners { // Draw the rest of the rounded rectangle path.addArc(tangent1End: point1, tangent2End: point2, radius: cornerRadius) } else { // Draw the rest of the rectangle path.addLine(to: point1) } path.closeSubpath() return path } func doInitSetup() { self.layer.addSublayer(shapeLayer) // Configure a shape layer to draw an outline shapeLayer.fillColor = UIColor.white.cgColor shapeLayer.strokeColor = UIColor.blue.cgColor shapeLayer.lineWidth = 2 } override init(frame: CGRect) { super.init(frame: frame) self.doInitSetup() } required init?(coder: NSCoder) { super.init(coder: coder) self.doInitSetup() } public func updateShapeLayerPath() { let path = buildShapeLayerPath() shapeLayer.path = path } override func layoutSubviews() { super.layoutSubviews() shapeLayer.frame = bounds updateShapeLayerPath() } } // example controller class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = UIColor(white: 0.95, alpha: 1.0) let v = ShapeWithNotchedTabView() v.translatesAutoresizingMaskIntoConstraints = false view.addSubview(v) NSLayoutConstraint.activate([ v.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0), v.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0), v.centerYAnchor.constraint(equalTo: view.centerYAnchor), v.heightAnchor.constraint(equalToConstant: 120.0), ]) let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.textAlignment = .center label.numberOfLines = 0 label.text = "Tap anywhere to toggle rounded corners" view.addSubview(label) NSLayoutConstraint.activate([ label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40.0), label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40.0), label.topAnchor.constraint(equalTo: v.bottomAnchor, constant: 20.0), ]) } override func touchesBegan(_ touches: Set, with event: UIEvent?) { if let v = view.subviews.first as? ShapeWithNotchedTabView { v.roundedCorners.toggle() } } }