@ViewBuilder Usage
in part 2 I discussed in detail how SwiftUI uses view builders to achieve a declarative DSL by flattening its child views into a single view. In this part, we’ll look at ways you can use ViewBuilder
in your own code.
let’s recap
1- if a single View is returned from the ViewBuilder
, it will do nothing and pass the single view back to whoever requested it. For example, the following View’s body will be of type `Text`.
2- if there’s more than one view that is being returned, the
ViewBuilder
will build aTupleView
, that is it will flatten the interface into a single view. For example, the following View’s body will be of type TupleView<Text, Image>
However, when I try this I get an error so it’s important to understand the difference to understand SwiftUI better.
SwiftUI body has ViewBuilder
Property wrapper attached to it in its protocol hence it can leverage the ability to flatten multiple children into a singular type which our subview
Computed Property or buildViews
function cannot. It will only work if both just return one view.
as you can see it can compile fine now
however, I want to break down my main view into subviews and have the ability to return multiple views from my computed property or function and not just a single view. how would I do that? There are 3 ways.
1- viewbuilder
with properties
adding @ViewBuilder
property wrapper now gives subView
the ability to flatten multiple child views. It will leverage the power of SwiftUI Dsl but if you don’t have @ViewBuilder
you cannot leverage the DSl so this is one way for you to create reusable views in the form of a property.
2- viewbuilder with functions
similarly, if you apply @ViewBuilder
property wrapper to a function it now gives buildViews
the ability to flatten multiple child views into one single View. You can also take parameters to do any customizations you need.
3- Create a new SubView struct and reuse it in your main View Struct to achieve the same result.
All of the above are ways that we can use the power of ViewBuilders
to separate your Views
into smaller reusable views.
if it makes sense to share your views in different parts of the app we can go by creating a new struct
otherwise, if it’s breaking views within the view we can use properties or functions with ViewBuilders
Important to Understand
Since the GermanHallo
Hello
and spells
views are parent views themselves with multiple children, they get unfolded when the stack iterates over its subviews like it did before and returns a single View which in this case Apple decides to return AnyView
. The purpose of AnyView is to erase the type information of the view inside it. I’ll go into more detail about AnyView in another article.
As you can see we return a tupleView and inside tupleView we get multiple single views abstracted as AnyView.
Important Gotcha
This is a special property of view lists: when a container view like the HStack iterates over the view list, nested lists are recursively unfolded so that a tree of tuple views turns into a flat list of views
Applying a view modifier to a function or property with @viewbuilder
with multiple children like below we add .background(.red)
to hello
property view leads to applying the view modifier to all it’s childviews. This is because for to the HStack, this is exactly the same as writing all views directly in the stack’s view builder closure. so the hello
View gets treated as 2 subviews when applying any view modifier.
we can also see that the stack’s spacing is equally applied between each of the views.
The same is also true if we use structs instead of functions.
However, there are exceptions you’ll find with Group.
Important Gotcha 2
A
ViewBuilder
function will only flatten the elements; it does not decide on the layout of these elements. This is the difference between returning two Views in a body (which will return a TupleView<ViewA, ViewB>) and an VStack with two views in it (which will return an VStack<TupleView<ViewA, ViewB>>.
looking at the example below since the body is marked as ViewBuilder
by default SwiftUI will flatten all the child views in a TupleView to return a single view but since we haven’t provided any layout like a Vstack
or Hstack
it will assume a default layout provided by the system which seems to be vertical however there will be no VStack or Hstack in the type.