Skip to content

Instantly share code, notes, and snippets.

@usagimaru
Last active November 14, 2018 19:22
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 usagimaru/7bf5f68ebc40ee8bf2ad15de3e40b0f2 to your computer and use it in GitHub Desktop.
Save usagimaru/7bf5f68ebc40ee8bf2ad15de3e40b0f2 to your computer and use it in GitHub Desktop.
A Swift extension of NSBezierPath for drawing the smooth rounded rectangles like iOS 7 icons.
import Cocoa
extension NSBezierPath {
var cgPath: CGPath {
// https://stackoverflow.com/questions/1815568/how-can-i-convert-nsbezierpath-to-cgpath
let path = CGMutablePath()
var points = [CGPoint](repeating: .zero, count: 3)
for i in 0 ..< self.elementCount {
let type = self.element(at: i, associatedPoints: &points)
switch type {
case .moveToBezierPathElement: path.move(to: points[0])
case .lineToBezierPathElement: path.addLine(to: points[0])
case .curveToBezierPathElement: path.addCurve(to: points[2], control1: points[0], control2: points[1])
case .closePathBezierPathElement: path.closeSubpath()
}
}
return path
}
convenience init(smoothRoundedRect rect: NSRect, cornerRadius: CGFloat) {
self.init()
// Original code is this PaintCode's blog post and the Objective-C category:
// https://www.paintcodeapp.com/news/code-for-ios-7-rounded-rectangles
let limitedRadius = min(cornerRadius, min(rect.size.width, rect.size.height) / 2.0 / 1.52866483)
func topLeft(x: CGFloat, y: CGFloat) -> NSPoint {
return NSPoint(x: rect.origin.x + x * limitedRadius, y: rect.origin.y + y * limitedRadius)
}
func topRight(x: CGFloat, y: CGFloat) -> NSPoint {
return NSPoint(x: rect.origin.x + rect.size.width - x * limitedRadius, y: rect.origin.y + y * limitedRadius)
}
func bottomLeft(x: CGFloat, y: CGFloat) -> NSPoint {
return NSPoint(x: rect.origin.x + x * limitedRadius, y: rect.origin.y + rect.size.height - y * limitedRadius)
}
func bottomRight(x: CGFloat, y: CGFloat) -> NSPoint {
return NSPoint(x: rect.origin.x + rect.size.width - x * limitedRadius, y: rect.origin.y + rect.size.height - y * limitedRadius)
}
func top(y: CGFloat) -> NSPoint {
return NSPoint(x: rect.midX, y: rect.origin.y + y * rect.size.width)
}
func bottom(y: CGFloat) -> NSPoint {
return NSPoint(x: rect.midX, y: rect.origin.y + rect.size.height - y * limitedRadius)
}
func left(x: CGFloat) -> NSPoint {
return NSPoint(x: rect.origin.x + x * rect.size.height, y: rect.midY)
}
func right(x: CGFloat) -> NSPoint {
return NSPoint(x: rect.origin.x + rect.size.width - x * limitedRadius, y: rect.midY)
}
func addSmoothRoundedRect1() {
self.move(to: topLeft(x: 1.52866483, y: 0))
self.line(to: topRight(x: 1.52866471, y: 0))
self.curve(to: topRight(x: 0.66993427, y: 0.06549600), controlPoint1: topRight(x: 1.08849323, y: 0), controlPoint2: topRight(x: 0.86840689, y: 0))
self.line(to: topRight(x: 0.63149399, y: 0.07491100))
self.curve(to: topRight(x: 0.07491176, y: 0.63149399), controlPoint1: topRight(x: 0.37282392, y: 0.16905899), controlPoint2: topRight(x: 0.16906013, y: 0.37282401))
self.curve(to: topRight(x: 0, y: 1.52866483), controlPoint1: topRight(x: 0, y: 0.86840701), controlPoint2: topRight(x: 0, y: 1.08849299))
self.line(to: bottomRight(x: 0, y: 1.52866471))
self.curve(to: bottomRight(x: 0.06549569, y: 0.66993493), controlPoint1: bottomRight(x: 0, y: 1.08849323), controlPoint2: bottomRight(x: 0, y: 0.86840689))
self.line(to: bottomRight(x: 0.07491111, y: 0.63149399))
self.curve(to: bottomRight(x: 0.63149399, y: 0.07491111), controlPoint1: bottomRight(x: 0.16905883, y: 0.37282392), controlPoint2: bottomRight(x: 0.37282392, y: 0.16905883))
self.curve(to: bottomRight(x: 1.52866471, y: 0), controlPoint1: bottomRight(x: 0.86840689, y: 0), controlPoint2: bottomRight(x: 1.08849323, y: 0))
self.line(to: bottomLeft(x: 1.52866483, y: 0))
self.curve(to: bottomLeft(x: 0.66993397, y: 0.06549569), controlPoint1: bottomLeft(x: 1.08849299, y: 0), controlPoint2: bottomLeft(x: 0.86840701, y: 0))
self.line(to: bottomLeft(x: 0.63149399, y: 0.07491111))
self.curve(to: bottomLeft(x: 0.07491100, y: 0.63149399), controlPoint1: bottomLeft(x: 0.37282401, y: 0.16905883), controlPoint2: bottomLeft(x: 0.16906001, y: 0.37282392))
self.curve(to: bottomLeft(x: 0, y: 1.52866471), controlPoint1: bottomLeft(x: 0, y: 0.86840689), controlPoint2: bottomLeft(x: 0, y: 1.08849323))
self.line(to: topLeft(x: 0, y: 1.52866483))
self.curve(to: topLeft(x: 0.06549600, y: 0.66993397), controlPoint1: topLeft(x: 0, y: 1.08849299), controlPoint2: topLeft(x: 0, y: 0.86840701))
self.line(to: topLeft(x: 0.07491100, y: 0.63149399))
self.curve(to: topLeft(x: 0.63149399, y: 0.07491100), controlPoint1: topLeft(x: 0.16906001, y: 0.37282401), controlPoint2: topLeft(x: 0.37282401, y: 0.16906001))
self.curve(to: topLeft(x: 1.52866483, y: 0), controlPoint1: topLeft(x: 0.86840701, y: 0), controlPoint2: topLeft(x: 1.08849299, y: 0))
self.close()
}
func addSmoothRoundedRect2a() {
self.move(to: topLeft(x: 2.00593972, y: 0))
self.line(to: NSPoint(x: rect.origin.x + rect.size.width - 1.52866483 * cornerRadius, y: rect.origin.y + 0 * cornerRadius))
self.curve(to: topRight(x: 0.99544263, y: 0.10012127), controlPoint1: topRight(x: 1.63527834, y: 0), controlPoint2: topRight(x: 1.29884040, y: 0))
self.line(to: topRight(x: 0.93667978, y: 0.11451437))
self.curve(to: topRight(x: 0.00000051, y: 1.45223188), controlPoint1: topRight(x: 0.37430558, y: 0.31920183), controlPoint2: topRight(x: 0.00000051, y: 0.85376567))
self.curve(to: right(x: 0), controlPoint1: right(x: 0), controlPoint2: right(x: 0))
self.line(to: right(x: 0))
self.curve(to: right(x: 0), controlPoint1: right(x: 0), controlPoint2: right(x: 0))
self.line(to: bottomRight(x: 0, y: 1.45223165))
self.curve(to: bottomRight(x: 0.93667978, y: 0.11451438), controlPoint1: bottomRight(x: 0, y: 0.85376561), controlPoint2: bottomRight(x: 0.37430558, y: 0.31920174))
self.curve(to: bottomRight(x: 2.30815363, y: 0), controlPoint1: bottomRight(x: 1.29884040, y: 0), controlPoint2: bottomRight(x: 1.63527834, y: 0))
self.line(to: NSPoint(x: rect.origin.x + 1.52866483 * cornerRadius, y: rect.origin.y + rect.size.height - 0 * limitedRadius))
self.curve(to: bottomLeft(x: 0.99544257, y: 0.10012124), controlPoint1: bottomLeft(x: 1.63527822, y: 0), controlPoint2: bottomLeft(x: 1.29884040, y: 0))
self.line(to: bottomLeft(x: 0.93667972, y: 0.11451438))
self.curve(to: bottomLeft(x: -0.00000001, y: 1.45223176), controlPoint1: bottomLeft(x: 0.37430549, y: 0.31920174), controlPoint2: bottomLeft(x: -0.00000007, y: 0.85376561))
self.curve(to: left(x: 0), controlPoint1: left(x: 0), controlPoint2: left(x: 0))
self.line(to: left(x: 0))
self.curve(to: left(x: 0), controlPoint1: left(x: 0), controlPoint2: left(x: 0))
self.line(to: topLeft(x: -0.00000001, y: 1.45223153))
self.curve(to: topLeft(x: 0.93667978, y: 0.11451436), controlPoint1: topLeft(x: 0.00000004, y: 0.85376537), controlPoint2: topLeft(x: 0.37430561, y: 0.31920177))
self.curve(to: topLeft(x: 2.30815363, y: 0), controlPoint1: topLeft(x: 1.29884040, y: 0), controlPoint2: topLeft(x: 1.63527822, y: 0))
self.line(to: NSPoint(x: rect.origin.x + 1.52866483 * cornerRadius, y: rect.origin.y + 0 * cornerRadius))
self.line(to: topLeft(x: 2.00593972, y: 0))
self.close()
}
func addSmoothRoundedRect2b() {
self.move(to: top(y: 0))
self.line(to: top(y: 0))
self.curve(to: top(y: 0), controlPoint1: top(y: 0), controlPoint2: top(y: 0))
self.line(to: topRight(x: 1.45223153, y: 0))
self.curve(to: topRight(x: 0.11451442, y: 0.93667936), controlPoint1: topRight(x: 0.85376573, y: 0.00000001), controlPoint2: topRight(x: 0.31920189, y: 0.37430537))
self.curve(to: topRight(x: 0, y: 2.30815387), controlPoint1: topRight(x: 0, y: 1.29884040), controlPoint2: topRight(x: 0, y: 1.63527822))
self.line(to: NSPoint(x: rect.origin.x + rect.size.width - 0 * cornerRadius, y: rect.origin.y + rect.size.height - 1.52866483 * cornerRadius))
self.curve(to: bottomRight(x: 0.10012137, y: 0.99544269), controlPoint1: bottomRight(x: 0, y: 1.63527822), controlPoint2: bottomRight(x: 0, y: 1.29884028))
self.line(to: bottomRight(x: 0.11451442, y: 0.93667972))
self.curve(to: bottomRight(x: 1.45223165, y: 0), controlPoint1: bottomRight(x: 0.31920189, y: 0.37430552), controlPoint2: bottomRight(x: 0.85376549, y: 0))
self.curve(to: bottom(y: 0), controlPoint1: bottom(y: 0), controlPoint2: bottom(y: 0))
self.line(to: bottom(y: 0))
self.curve(to: bottom(y: 0), controlPoint1: bottom(y: 0), controlPoint2: bottom(y: 0))
self.line(to: bottomLeft(x: 1.45223141, y: 0))
self.curve(to: bottomLeft(x: 0.11451446, y: 0.93667972), controlPoint1: bottomLeft(x: 0.85376543, y: 0), controlPoint2: bottomLeft(x: 0.31920192, y: 0.37430552))
self.curve(to: bottomLeft(x: 0, y: 2.30815387), controlPoint1: bottomLeft(x: 0, y: 1.29884028), controlPoint2: bottomLeft(x: 0, y: 1.63527822))
self.line(to: NSPoint(x: rect.origin.x + 0 * cornerRadius, y: rect.origin.y + 1.52866483 * cornerRadius))
self.curve(to: topLeft(x: 0.10012126, y: 0.99544257), controlPoint1: topLeft(x: 0, y: 1.63527822), controlPoint2: topLeft(x: 0, y: 1.29884040))
self.line(to: topLeft(x: 0.11451443, y: 0.93667966))
self.curve(to: topLeft(x: 1.45223153, y: 0), controlPoint1: topLeft(x: 0.31920189, y: 0.37430552), controlPoint2: topLeft(x: 0.85376549, y: 0))
self.curve(to: top(y: 0), controlPoint1: top(y: 0), controlPoint2: top(y: 0))
self.line(to: top(y: 0))
self.close()
}
func addSmoothRoundedRect3() {
self.move(to: top(y: 0))
self.line(to: top(y: 0))
self.curve(to: top(y: 0), controlPoint1: top(y: 0), controlPoint2: top(y: 0))
self.line(to: top(y: 0))
self.curve(to: topRight(x: 0, y: 1.52866483), controlPoint1: topRight(x: 0.68440646, y: 0.00000001), controlPoint2: topRight(x: 0, y: 0.68440658))
self.curve(to: topRight(x: 0, y: 1.52866507), controlPoint1: topRight(x: 0, y: 1.52866495), controlPoint2: topRight(x: 0, y: 1.52866495))
self.curve(to: topRight(x: 0, y: 1.52866483), controlPoint1: topRight(x: 0, y: 1.52866483), controlPoint2: topRight(x: 0, y: 1.52866483))
self.line(to: right(x: 0))
self.curve(to: bottomRight(x: 0, y: 1.52866471), controlPoint1: bottomRight(x: 0, y: 1.52866471), controlPoint2: bottomRight(x: 0, y: 1.52866471))
self.line(to: bottomRight(x: 0, y: 1.52866471))
self.curve(to: bottom(y: 0), controlPoint1: bottomRight(x: 0, y: 0.68440646), controlPoint2: bottomRight(x: 0.68440646, y: 0))
self.curve(to: bottom(y: 0), controlPoint1: bottom(y: 0), controlPoint2: bottom(y: 0))
self.curve(to: bottom(y: 0), controlPoint1: bottom(y: 0), controlPoint2: bottom(y: 0))
self.line(to: bottom(y: 0))
self.curve(to: bottom(y: 0), controlPoint1: bottom(y: 0), controlPoint2: bottom(y: 0))
self.line(to: bottom(y: 0))
self.curve(to: bottomLeft(x: 0, y: 1.52866471), controlPoint1: bottomLeft(x: 0.68440646, y: 0), controlPoint2: bottomLeft(x: -0.00000004, y: 0.68440646))
self.curve(to: bottomLeft(x: 0, y: 1.52866495), controlPoint1: bottomLeft(x: 0, y: 1.52866471), controlPoint2: bottomLeft(x: 0, y: 1.52866495))
self.curve(to: bottomLeft(x: 0, y: 1.52866471), controlPoint1: bottomLeft(x: 0, y: 1.52866471), controlPoint2: bottomLeft(x: 0, y: 1.52866471))
self.line(to: left(x: 0))
self.curve(to: topLeft(x: 0, y: 1.52866483), controlPoint1: topLeft(x: 0, y: 1.52866483), controlPoint2: topLeft(x: 0, y: 1.52866483))
self.line(to: topLeft(x: 0, y: 1.52866471))
self.curve(to: top(y: 0), controlPoint1: topLeft(x: 0.00000007, y: 0.68440652), controlPoint2: topLeft(x: 0.68440658, y: -0.00000001))
self.curve(to: top(y: 0), controlPoint1: top(y: 0), controlPoint2: top(y: 0))
self.line(to: top(y: 0))
self.close()
}
func addSmoothRoundedRect3a() {
self.move(to: top(y: 0))
self.line(to: top(y: 0))
self.curve(to: top(y: 0), controlPoint1: top(y: 0), controlPoint2: top(y: 0))
self.line(to: top(y: 0))
self.curve(to: topRight(x: 0, y: 1.52866483), controlPoint1: topRight(x: 0.68440646, y: 0.00000001), controlPoint2: topRight(x: 0, y: 0.68440658))
self.curve(to: topRight(x: 0, y: 1.52866507), controlPoint1: topRight(x: 0, y: 1.52866495), controlPoint2: topRight(x: 0, y: 1.52866495))
self.curve(to: topRight(x: 0, y: 1.52866483), controlPoint1: topRight(x: 0, y: 1.52866483), controlPoint2: topRight(x: 0, y: 1.52866483))
self.line(to: right(x: 0))
self.curve(to: bottomRight(x: 0, y: 1.52866471), controlPoint1: bottomRight(x: 0, y: 1.52866471), controlPoint2: bottomRight(x: 0, y: 1.52866471))
self.line(to: bottomRight(x: 0, y: 1.52866471))
self.curve(to: bottom(y: 0), controlPoint1: bottomRight(x: 0, y: 0.68440646), controlPoint2: bottomRight(x: 0.68440646, y: 0))
self.curve(to: bottom(y: 0), controlPoint1: bottom(y: 0), controlPoint2: bottom(y: 0))
self.curve(to: bottom(y: 0), controlPoint1: bottom(y: 0), controlPoint2: bottom(y: 0))
self.line(to: bottom(y: 0))
self.curve(to: bottom(y: 0), controlPoint1: bottom(y: 0), controlPoint2: bottom(y: 0))
self.line(to: bottom(y: 0))
self.curve(to: bottomLeft(x: 0, y: 1.52866471), controlPoint1: bottomLeft(x: 0.68440646, y: 0), controlPoint2: bottomLeft(x: -0.00000004, y: 0.68440646))
self.curve(to: bottomLeft(x: 0, y: 1.52866495), controlPoint1: bottomLeft(x: 0, y: 1.52866471), controlPoint2: bottomLeft(x: 0, y: 1.52866495))
self.curve(to: bottomLeft(x: 0, y: 1.52866471), controlPoint1: bottomLeft(x: 0, y: 1.52866471), controlPoint2: bottomLeft(x: 0, y: 1.52866471))
self.line(to: left(x: 0))
self.curve(to: topLeft(x: 0, y: 1.52866483), controlPoint1: topLeft(x: 0, y: 1.52866483), controlPoint2: topLeft(x: 0, y: 1.52866483))
self.line(to: topLeft(x: 0, y: 1.52866471))
self.curve(to: top(y: 0), controlPoint1: topLeft(x: 0.00000007, y: 0.68440652), controlPoint2: topLeft(x: 0.68440658, y: -0.00000001))
self.curve(to: top(y: 0), controlPoint1: top(y: 0), controlPoint2: top(y: 0))
self.line(to: top(y: 0))
self.close()
}
func addSmoothRoundedRect3b() {
self.move(to: top(y: 0))
self.line(to: top(y: 0))
self.curve(to: topRight(x: 1.52866495, y: 0), controlPoint1: topRight(x: 1.52866495, y: 0), controlPoint2: topRight(x: 1.52866495, y: 0))
self.line(to: topRight(x: 1.52866495, y: 0))
self.curve(to: right(x: 0), controlPoint1: topRight(x: 0.68440676, y: 0.00000001), controlPoint2: topRight(x: 0, y: 0.68440658))
self.curve(to: right(x: 0), controlPoint1: right(x: 0), controlPoint2: right(x: 0))
self.curve(to: right(x: 0), controlPoint1: right(x: 0), controlPoint2: right(x: 0))
self.line(to: right(x: 0))
self.curve(to: right(x: 0), controlPoint1: right(x: 0), controlPoint2: right(x: 0))
self.line(to: right(x: 0))
self.curve(to: bottomRight(x: 1.52866495, y: 0), controlPoint1: bottomRight(x: 0, y: 0.68440652), controlPoint2: bottomRight(x: 0.68440676, y: 0))
self.curve(to: bottomRight(x: 1.52866495, y: 0), controlPoint1: bottomRight(x: 1.52866495, y: 0), controlPoint2: bottomRight(x: 1.52866495, y: 0))
self.curve(to: bottomRight(x: 1.52866495, y: 0), controlPoint1: bottomRight(x: 1.52866495, y: 0), controlPoint2: bottomRight(x: 1.52866495, y: 0))
self.line(to: bottom(y: 0))
self.curve(to: bottomLeft(x: 1.52866483, y: 0), controlPoint1: bottomLeft(x: 1.52866483, y: 0), controlPoint2: bottomLeft(x: 1.52866483, y: 0))
self.line(to: bottomLeft(x: 1.52866471, y: 0))
self.curve(to: left(x: 0), controlPoint1: bottomLeft(x: 0.68440646, y: 0), controlPoint2: bottomLeft(x: -0.00000004, y: 0.68440676))
self.curve(to: left(x: 0), controlPoint1: left(x: 0), controlPoint2: left(x: 0))
self.curve(to: left(x: 0), controlPoint1: left(x: 0), controlPoint2: left(x: 0))
self.line(to: left(x: 0))
self.curve(to: left(x: 0), controlPoint1: left(x: 0), controlPoint2: left(x: 0))
self.line(to: left(x: 0))
self.curve(to: topLeft(x: 1.52866483, y: 0), controlPoint1: topLeft(x: 0.00000007, y: 0.68440652), controlPoint2: topLeft(x: 0.68440664, y: -0.00000001))
self.curve(to: topLeft(x: 1.52866483, y: 0), controlPoint1: topLeft(x: 1.52866483, y: 0), controlPoint2: topLeft(x: 1.52866483, y: 0))
self.line(to: top(y: 0))
self.close()
}
let r = 1.52866495 * 2 * cornerRadius
if (rect.size.width > r) && (rect.size.height > r) {
addSmoothRoundedRect1()
}
else if rect.size.width > r {
addSmoothRoundedRect2a()
}
else if rect.size.height > r {
addSmoothRoundedRect2b()
}
else if rect.size.height > rect.size.width {
addSmoothRoundedRect3a()
}
else {
addSmoothRoundedRect3b()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment