Does anyone know how to make a shape like this?
I followed apples tutorial and came up with this but it seems overly complicated for what I want to do and not perfectly rounded. It also seems hard to implement and maintain to me. Seems like there is a simpler way. Thank you for your thoughts.
struct HexagonParameters {
struct Segment {
let line: CGPoint
let curve: CGPoint
let control: CGPoint
}
static let adjustment: CGFloat = 0.085
static let topSholdHt = 0.13
static let botSholdHt = 0.87
static let segments = [
Segment( // top center
line: CGPoint(x: 0.80, y: topSholdHt), // top right
curve: CGPoint(x: 0.20, y: topSholdHt), // top left
control: CGPoint(x: 0.50, y: 0.00) // center / peak
),
Segment( // upper left
line: CGPoint(x: 0.05, y: 0.20),
curve: CGPoint(x: 0.00, y: 0.30),
control: CGPoint(x: 0.0, y: 0.23)
),
Segment( // lower left
line: CGPoint(x: 0.00, y: 0.70),
curve: CGPoint(x: 0.05, y: 0.80),
control: CGPoint(x: 0.00, y: 0.78)
),
Segment( // bottom center
line: CGPoint(x: 0.20, y: botSholdHt),
curve: CGPoint(x: 0.80, y: botSholdHt),
control: CGPoint(x: 0.50, y: 1.00)
),
Segment( // lower right
line: CGPoint(x: 0.95, y: 0.80),
curve: CGPoint(x: 1.00, y: 0.70),
control: CGPoint(x: 1.00, y: 0.78)
),
Segment(
line: CGPoint(x: 1.00, y: 0.30),
curve: CGPoint(x: 0.95, y: 0.20),
control: CGPoint(x: 1.00, y: 0.23)
)
]
}
The struct is used like this
struct ContentView: View {
var body: some View {
Path { path in
var width: CGFloat = 300.0
let height = width
path.move(
to: CGPoint(
x: width * 0.95,
y: height * 0.20
)
)
HexagonParameters.segments.forEach { segment in
path.addLine(
to: CGPoint(
x: width * segment.line.x,
y: height * segment.line.y
)
)
path.addQuadCurve(
to: CGPoint(
x: width * segment.curve.x,
y: height * segment.curve.y),
control: CGPoint(
x: width * segment.control.x,
y: height * segment.control.y
)
)
}
}
.stroke(style: StrokeStyle(lineWidth: 2))
.foregroundColor(Color.black)
.padding(.horizontal)
}
}
As I said in my comment, it looks like you solved it yourself, your code produces the shape in the question.
However, you remarked that your shape was:
not perfectly rounded
So I am wondering if you are trying to achieve a smoother shape which does not have the inverted arcs near the corners?
Assuming this is the case, here is how it could be solved with a custom Shape
. I wouldn't say it's any simpler to maintain than what you originally had, but at least it demonstrates a different approach.
struct Lozenge: Shape {
func path(in rect: CGRect) -> Path {
let cornerRadius = min(rect.size.width, rect.size.height) * 0.1
let middleHeight = rect.size.height * 0.15
let cornerAngleRadians = atan2(rect.size.width, rect.size.height - (2 * middleHeight))
let cornerAngleDegrees = cornerAngleRadians * 180 / Double.pi
var path = Path()
var x = rect.minX
var y = rect.minY + cornerRadius + middleHeight
path.move(to: CGPoint(x: x, y: y))
// Top-left corner
x += cornerRadius
path.addArc(
center: CGPoint(x: x, y: y),
radius: cornerRadius,
startAngle: .degrees(-180),
endAngle: .degrees(-180 + cornerAngleDegrees),
clockwise: false
)
// Middle bump on top
x = rect.maxX - cornerRadius + (cornerRadius * cos(cornerAngleRadians))
y = rect.minY + middleHeight + cornerRadius - (cornerRadius * sin(cornerAngleRadians))
path.addQuadCurve(
to: CGPoint(x: x, y: y),
control: CGPoint(x: rect.midX, y: rect.minY - middleHeight)
)
// Top-right corner
x = rect.maxX - cornerRadius
y = rect.minY + cornerRadius + middleHeight
path.addArc(
center: CGPoint(x: x, y: y),
radius: cornerRadius,
startAngle: .degrees(-cornerAngleDegrees),
endAngle: .degrees(0),
clockwise: false
)
// Bottom-right corner
y = rect.maxY - cornerRadius - middleHeight
path.addArc(
center: CGPoint(x: x, y: y),
radius: cornerRadius,
startAngle: .degrees(0),
endAngle: .degrees(cornerAngleDegrees),
clockwise: false
)
// Lower bump on bottom
x = rect.minX + cornerRadius - (cornerRadius * cos(cornerAngleRadians))
y = rect.maxY - middleHeight - cornerRadius + (cornerRadius * sin(cornerAngleRadians))
path.addQuadCurve(
to: CGPoint(x: x, y: y),
control: CGPoint(x: rect.midX, y: rect.maxY + middleHeight)
)
// Bottom-left corner
x = rect.minX + cornerRadius
y = rect.maxY - cornerRadius - middleHeight
path.addArc(
center: CGPoint(x: x, y: y),
radius: cornerRadius,
startAngle: .degrees(180 - cornerAngleDegrees),
endAngle: .degrees(180),
clockwise: false
)
path.closeSubpath()
return path
}
}
struct ContentView: View {
var body: some View {
Lozenge()
.stroke(lineWidth: 3)
.frame(width: 300, height: 250)
.background(.yellow)
}
}