Creating custom shapes in SwiftUI is pretty easy. At a high level, to satisfy the Shape protocol you just need to define path(in rect: CGRect) -> Path
. Shape itself conforms to View, so you can even get a preview in XCode of your shape while you build it.
We’ll call this shape Hexagon, but really we’re building a regular hexagon, meaning that all sides and angles are coequal. You may be tempted to try to build a hexagon of six equilateral triangles, but it’s actually simpler with a bit of trigonometry. Each corner can be calculated by taking the center point + radius × cosine(π × index of the point) for the x coordinate and center point + radius × sine(π × index of the point) for the y coordinate.
Since we’re given a bounding rectangle, we find the center of that rectangle to be the center of the hexagon:
let center = CGPoint(x: rect.midX, y: rect.midY)
The radius is the smaller of the height or width, divided by two:
let radius = min(rect.size.height, rect.size.width) / 2
We can use those to calculate the corners, returning an array of CGPoint
for each vertex of the hexagon:
func corners(center: CGPoint, radius: CGFloat) -> [CGPoint] {
var points: [CGPoint] = []
for i in (0...5) {
let angle = CGFloat.pi / 3 * i
let point = CGPoint(
x: center.x + radius * cos(angle),
y: center.y + radius * sin(angle)
)
points.append(point)
}
return points
}
Finally, to actually build the Path that’s required from the path
function, we’ll create a path (var path = Path()
), move to the first vertex: path.move(to: corners[0]
, and then iterate over the points, calling path.addLine(to: point)
for each one. Finally, we can call path.closeSubpath()
to complete the outline and return the path.
Pulling everything together:
struct Hexagon: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
let center = CGPoint(x: rect.midX, y: rect.midY)
let radius = min(rect.size.height, rect.size.width) / 2
let corners = corners(center: center, radius: radius)
path.move(to: corners[0])
corners[1...5].forEach() { point in
path.addLine(to: point)
}
path.closeSubpath()
return path
}
func corners(center: CGPoint, radius: CGFloat) -> [CGPoint] {
var points: [CGPoint] = []
for i in (0...5) {
let angle = CGFloat.pi / 3 * CGFloat(i)
let point = CGPoint(
x: center.x + radius * cos(angle),
y: center.y + radius * sin(angle)
)
points.append(point)
}
return points
}
}
Shapes can be used in SwiftUI either directly, being added as a View right into another view, to clip an image (say, if you wanted to prove that you spent too much money and electricity on a URL to an image), and can be colored, resized, etc.
Top comments (0)