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.