Exploring SwiftUI Shapes: Utilizing @ViewBuilder for Dynamic View Creation

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.

share this post with friends

Picture of Nicola De filippo

Nicola De filippo

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve

Leave a comment

Your email address will not be published. Required fields are marked *

Who I am

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve.

Follow Me Here

Get The Latest Updates

Periodically receive my super contents on coding and programming

join the family;)

Recent Posts