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.