Here, we have a background view applied to the button, filled with a colour. How does the layout process look now? Well, it’s similar to how it was before.

The parent view passes down its size to the child view, which is the background view (also known as a modifier view, which we’ll learn more about later in this post).

Since the background view is layout-neutral, it passes that size down to its child view, which is the button.

The button decides to keep the same frame as earlier, so it passes the size up to the background view.

The background view passes this size up to the parent view, but before it does, it also passes that size to its secondary child view, which is the colour view.

Colours always obey whatever size given to them, so in this case, its the same size as the background (which is the same size as the button). The background then passes the size up to the parent view.

Finally, the parent view positions the background view in its own coordinate space and centres it as before.

In case you were wondering — yes, there are no layout constraints available to us, like in UIKit. Everything is done using stacks, frames, paddings, spacers, etc. The reason why is because SwiftUI layout isn’t a constraint system.

View Builder

Now that we’ve understood what views are and how its laid out on the screen, let’s take a look at how views are declared.

In SwiftUI, a view heirarchy is declared declaratively, rather than imperatively. In other words, your code describes the user interface and SwiftUI does the heavy lifting to figure out how to draw that on the screen.

Here’s a simple example:

Here, we have a stack of two views — a label and a button, which contains also contains a label, which gives the button its title.

In this example, we’ve declared the hierarchical relationship between the these views, by assembling them into a structure that stores that information and lets SwiftUI render it on the screen.

We created this structure with the help of a container view. Container views allow for easy composition of views, for example, by letting you place one view inside of another (and so on).

How does a container view work? Well, to understand it, let’s take a look at one of many container views available in SwiftUI — a vertical stack.

A vertical stack (or a VStack ) is a container view that allows us to place multiple child views, all stacked vertically.

The way you add views to the stack is by placing them inside a closure. However, this is not an ordinary closure — this is a closure marked with a special attribute, called @ViewBuilder .

This attribute is a function builder (see previous post for details), that “collects” all the views inside the closure and “reduces” it into a single view (you can think of it as a way of allowing us to represent a view as a function of its inputs).

It also helps us describe the hierarchal relationship between views by using braces & indentation to separate the container view, child views and their modifiers from each other.

@ViewBuilder closures only support a maximum of 10 child views, due to Swift’s lack of variadic generics. You can work around it by either moving the extra child views into their own container (and so on) or extending ViewBuilder with extra buildBlock() methods that can accept more than 10 child views.

Writing this in UIKit would’ve taken us a lot longer, having to explicitly describe each step and calling APIs like addSubview() . This is very tedious and a small mistake could easily ruin the entire result.

By creating our views declaratively, we can focus on describing what we want to see and letting SwiftUI figure how to do it and you can be sure that you’re going to get a high quality result.

View Modifiers

The last thing to talk about is view modifiers. Just like a View , a ViewModifer is also a protocol, with a single requirement. But, instead of a body property, it requires a body method that takes in a View , and returns another View by “modifying” it in some way.

Here’s a simple example:

In this example, we’re using two built-in modifiers — padding and background. The padding modifier takes the padding amount as an argument and returns a new padded view. By default, it will apply the same padding on all directions, but you can customise that if you want.

The second modifier is background, which takes a view as a parameter and returns a new view with the view passed as argument as the background. In this example, we’re using the Color view, which simply provides a view whose background is a solid colour of our choice.

We can chain these modifiers together to create the modified view and the order in which we chain enforces a deterministic order in which the views are modified.

For example, here’s what it would look like if we put the padding after the background:

This is a bit strange, isn’t it? Well actually, the padding is still on the screen, it’s just that we can’t see it.

The reason we can’t see the padding is because the background modifier is only wrapping the text, not the padding. This means our padding is being applied outside of the background.

This ordering behaviour is important, because otherwise it would be impossible to know in which order the modifiers are applied, unless we spent a lot of time reading the documentation or manually testing each combination of ordering.

As mentioned earlier, we can create our own modifiers by simply conforming to the ViewModifier protocol. This is handy when we’re applying the same modification to a bunch of views, as we can encapsulate the behaviour into a single place and apply it to any view we like.

For example, if we had to style the look of text in the container the same, then instead of duplicating the code for each text, we could simply create a modifier that applies that look to each text:

View modifiers are a really cool way to apply simple or advanced modifications to views and chaining them to create even more complex modifications.