By now we should be used to the idea that all views in SwiftUI choose their own size, for example a Text
view has the size required to render the string provided:
struct ContentView : View {
var body: some View {
Text("Nogitsune Takeshi")
.font(.title)
}
}
Creates a view with the exact bounds necessary:

We also showed that the .frame
modifier actually creates a new view with the dimensions specifies, and positions the Text
view within it, such that:
struct ContentView : View {
var body: some View {
Text("Nogitsune Takeshi")
.font(.title)
.frame(width: 200, height: 200)
}
}
Actually creates two views, a .frame
that is 200×200 in size, and a Text
within it with the exact bounds necessary to render its contents:

We looked into this process further in flexible frames, introduced the concept of layout neutral views that choose their own size based on their children, and showed that in either dimension .frame
can have a fixed size, be layout neutral, or through minimum and maximum size constraints base its own size on that proposed by its own parent.
We’ll now take a look at another useful modifier view, one that adds padding around its child view, and has a number of different forms that we can use:
func padding(_ length: CGFloat) -> some View
func padding(_ insets: EdgeInsets) -> some View
func padding(_ edges: Edge.Set = .all, _ length: CGFloat? = nil) -> some View
The first form sets the padding of all edges to the length specified.
The second form sets the padding of each of the edges to the specific individual values you specify through the EdgeInsets
value.
The third form sets the padding of the set of edges you specify to the length supplied, leaving other edges unpadded. The third form also allows you to specify nil
as the length, instead of zero, which instructs SwiftUI to use a system default amount of padding appropriate for the situation.
Default values for all parameters of the third form are provided, which uses the system default padding for all edges, we’ll use that in our example:
struct ContentView : View {
var body: some View {
Text("Nogitsune Takeshi")
.font(.title)
.padding()
}
}
The .padding
modifier is no different from modifiers like .frame
, it doesn’t modify the Text
in any way, it instead creates a new view that adds padding, and positions the Text
view inside it as a child:

The layout process is actually a little more interesting than just adding padding, and is almost but not quite layout neutral. It in fact works something like we see for stacks when considering spacing.
- Parent proposes a size to the padding view.
- Padding view subtracts the appropriate padding length from each edge.
- Padding view proposes this smaller size to its child.
- Child chooses its size.
- Padding view takes the child’s size, adds the appropriate padding length back to each edge, and chooses that as its own size.
- Parent positions the padding view within its bounds.
Thus a .padding
view always tightly wraps its child (aside from the padding itself), with both being positioned by the parent frame, but at the same time adds an additional constraint (the padding) to the size the child can be.
We can demonstrate this by placing the .padding
inside a frame:
struct ContentView : View {
var body: some View {
Text("Nogitsune Takeshi")
.font(.title)
.padding()
.frame(width: 200, height: 200)
}
}
The .frame
has a fixed size of 200×200, the .padding
view subtracts the system default padding of 16px (in this case) from each side, and supplies the Text
with a proposed size of 168×168.
That’s too small for Text
to layout on one line, but still enough room to wrap over two lines, so it returns its size appropriately to do that. .padding
adds back the padding before returning its size, and the .frame
positions the padding view inside it.

As we can see, the padding view still tightly wraps the Text
, it isn’t increased in height or width to try and fill the parent frame, and is centered within it instead.