In this post, we explore how to use the @ViewBuilder annotation. It’s particularly useful when we need a function to return a View. While it’s a simple concept, it’s a classic example of something straightforward yet not always well-known.
In our example, we aim to showcase all the basic shapes that can be created in SwiftUI, including Rectangle, RoundedRectangle, Uneven Rounded Rectangle, Ellipse, Capsule, and Circle, as demonstrated here:

First, create an enum to represent the different shapes:
enum Shapes {
case Rectangle, RoundedRectangle, Uneven, Capsule, Ellipse, Circle
}
Now, define the structure to store the information about the shape:
struct ShapeDescription: Identifiable {
let id = UUID()
var type: Shapes
var name: String
}
In the view, create an array containing all the shape information:
struct ContentView: View {
var shapes: Array<ShapeDescription> =
[ShapeDescription(type: .Rectangle, name: "Rectangle"), ShapeDescription(type: .RoundedRectangle, name: "Rounded Rectangle"), ShapeDescription(type: .Uneven, name: "Uneven Rounded Rectangle"), ShapeDescription(type: .Capsule, name: "Capsule"),
ShapeDescription(type: .Ellipse, name: "Ellipse"),
ShapeDescription(type: .Circle, name: "Circle")]
To display all the information, use a list:
var body: some View {
NavigationStack {
List(shapes) { shape in
HStack {
Text(shape.name)
}
}
}
}
}
Currently, we only display the name. To show the shapes, one basic approach could be to add a switch statement based on the shape type within the list, but this would be inefficient. Instead, let’s create a function that returns a shape corresponding to its type:
NavigationStack {
List(shapes) { shape in
HStack {
getShape(shape: shape.type)
Text(shape.name)
}
}
}
Take a look at the getShape function:
@ViewBuilder
func getShape(shape: Shapes) -> some View {
switch(shape) {
case .Rectangle:
Rectangle()
.fill(.gray)
.frame(width: 40, height: 40)
case .RoundedRectangle:
RoundedRectangle(cornerRadius: 10)
.fill(.red)
.frame(width: 40, height: 40)
case .Uneven:
UnevenRoundedRectangle(cornerRadii: .init(topLeading: 50, topTrailing: 50))
.fill(.orange)
.frame(width: 40, height: 40)
case .Capsule:
Capsule()
.fill(.green)
.frame(width: 40, height: 20)
case .Ellipse:
Ellipse()
.fill(.blue)
.frame(width: 40, height: 20)
case .Circle:
Circle()
.fill(.purple)
.frame(width: 40, height: 40)
}
}
Use the @ViewBuilder annotation to specify that the function ‘builds’ a view, in fact returning some View. This function simply contains a switch statement to return the appropriate shape.
That’s all. Enjoy and stay tuned.