Custom Shapes in SwiftUI
Creating custom shapes in SwiftUI allows you to draw unique graphics and build creative user interfaces. By conforming to the Shape protocol, you can create reusable, scalable vector graphics that integrate seamlessly with SwiftUI's rendering system.
What are Custom Shapes?
Custom shapes in SwiftUI are types that conform to the Shape protocol. They define a path that SwiftUI can fill, stroke, or use for clipping. Shapes are resolution-independent and scale beautifully.
Creating a Basic Custom Shape
Here's how to create a simple triangle shape:
import SwiftUI
struct Triangle: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: rect.midX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.closeSubpath()
return path
}
}
struct ContentView: View {
var body: some View {
Triangle()
.fill(Color.blue)
.frame(width: 200, height: 200)
}
}
Advanced Custom Shape: Star
Here's a more complex example creating a star shape:
struct Star: Shape {
let points: Int
let smoothness: Double
func path(in rect: CGRect) -> Path {
let center = CGPoint(x: rect.width / 2, y: rect.height / 2)
let outerRadius = min(rect.width, rect.height) / 2
let innerRadius = outerRadius * smoothness
var angle: Double = -.pi / 2
let angleIncrement = .pi * 2 / Double(points * 2)
var path = Path()
for point in 0..<points * 2 {
let radius = point.isMultiple(of: 2) ? outerRadius : innerRadius
let x = center.x + CGFloat(cos(angle)) * radius
let y = center.y + CGFloat(sin(angle)) * radius
if point == 0 {
path.move(to: CGPoint(x: x, y: y))
} else {
path.addLine(to: CGPoint(x: x, y: y))
}
angle += angleIncrement
}
path.closeSubpath()
return path
}
}
struct StarView: View {
var body: some View {
Star(points: 5, smoothness: 0.45)
.fill(Color.yellow)
.frame(width: 200, height: 200)
}
}
Animatable Shapes
You can make shapes animatable by conforming to Animatable:
struct AnimatableStar: Shape {
var points: Double
var animatableData: Double {
get { points }
set { points = newValue }
}
func path(in rect: CGRect) -> Path {
Star(points: Int(points), smoothness: 0.45).path(in: rect)
}
}
struct AnimatedStarView: View {
@State private var points: Double = 5
var body: some View {
VStack {
AnimatableStar(points: points)
.fill(Color.blue)
.frame(width: 200, height: 200)
.animation(.easeInOut, value: points)
Slider(value: $points, in: 3...10, step: 1)
}
.padding()
}
}
Use Cases
Custom shapes are perfect for:
-
Creating unique UI elements
-
Building custom charts and graphs
-
Designing logos and icons
-
Creating progress indicators
-
Building custom buttons and controls
Wrap up
The Shape protocol is straightforward — implement path(in:) and SwiftUI handles the rest. Custom shapes scale infinitely and work with any fill, stroke, or trim modifier.
Resources:
Read more
- Understanding Sendable in Swift • 3 minutes reading time.
- CocoaPods Trunk Read-only Plan • 2 minutes reading time.
- Xcode shortcuts • 3 minutes reading time.
Share
Share Bluesky Mastodon Twitter LinkedIn Facebook