Skip to content

Instantly share code, notes, and snippets.

@robertmryan
Last active February 16, 2019 18:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robertmryan/14f1d5593fe81b575927229f287f038e to your computer and use it in GitHub Desktop.
Save robertmryan/14f1d5593fe81b575927229f287f038e to your computer and use it in GitHub Desktop.
@IBDesignable
class CurveIndentedView: UIView {
@IBInspectable
var radius: CGFloat = 50 { didSet { updatePath() } }
private var shapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = UIColor.red.cgColor
return shapeLayer
}()
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
override func layoutSubviews() {
super.layoutSubviews()
updatePath()
}
}
private extension CurveIndentedView {
func configure() {
layer.addSublayer(shapeLayer)
}
func updatePath() {
let angle: CGFloat = .pi / 6 // adjust this to whatever you want, between 0 and `.pi / 2`; /6 or /8 are close
let controlPointOffset = radius / cos(angle)
let endPointOffset = controlPointOffset + sin(angle) * radius
let path = UIBezierPath()
path.move(to: CGPoint(x: bounds.midX - endPointOffset, y: bounds.minY))
path.addLine(to: CGPoint(x: bounds.minX, y: bounds.minY))
path.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY))
path.addLine(to: CGPoint(x: bounds.midX + endPointOffset, y: bounds.minY))
var endPoint = CGPoint(x: bounds.midX + cos(angle) * radius, y: bounds.minY + sin(angle) * radius)
var controlPoint = CGPoint(x: bounds.midX + controlPointOffset, y: bounds.minY)
path.addQuadCurve(to: endPoint, controlPoint: controlPoint)
path.addArc(withCenter: CGPoint(x: bounds.midX, y: bounds.minY), radius: radius, startAngle: angle, endAngle: .pi - angle, clockwise: true)
endPoint = CGPoint(x: bounds.midX - endPointOffset, y: bounds.minY)
controlPoint = CGPoint(x: bounds.midX - controlPointOffset, y: bounds.minY)
path.addQuadCurve(to: endPoint, controlPoint: controlPoint)
path.close()
shapeLayer.path = path.cgPath
}
}
@robertmryan
Copy link
Author

That yields:
img_0399

@robertmryan
Copy link
Author

robertmryan commented Feb 16, 2019

You can then marry that with a rounded corner button:

@IBDesignable
class RoundedCornerButton: UIButton {

    override func layoutSubviews() {
        super.layoutSubviews()
        layer.cornerRadius = min(bounds.width, bounds.height) / 2
        clipsToBounds = true
    }

}

If you then size and position that appropriately, that yields:

screen shot 2019-02-16 at 10 39 58 am

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment