The SwiftUI Field Guidebeta

Flexible Frames

A flexible frame allows us to specify a minimum, ideal, and maximum value for each dimension. We can think of a frame as a “wrapper” view around its child that can both change the proposal to its child and change the reported size. One the most common usages is using maxWidth: .infinity to fill up the available width:

Code
Text("Checkout")    .frame(maxWidth: .infinity)    .padding(8)    .background { Color.accentColor }
Preview
200

Frames align their content using the alignment parameter. The default is center. When we combine alignment with maxWidth: .infinity, we can push a view to the leading or trailing edge.

Code
Text("Hello, world")    .frame(maxWidth: .infinity)
Preview
leadingcentertrailing
200

We can also use a flexible frame to specify a minimum width. Note that this frame does not change the width of the text itself; it only wraps the text in a frame that's at least 100 points wide. If the text is wider than 100 points, the frame will be wider than 100 points as well. The background then becomes the reported size of the frame.

Code
Text("Hello")    .padding(16)    .frame(minWidth: 150)    .background { Color.accentColor }
Preview
200

At first sight, it looks like the frame below accepts the proposed width unconditionally. However, the behavior is a little bit more subtle. For example, in the view below, the text always renders at its ideal size. The frame becomes at least the proposed width, but if the proposed width is small enough, it becomes as wide as the content. When the view is narrow enough, we can see it drawing out of bounds.

Code
Text("Hello, world")    .fixedSize()    .frame(maxWidth: .infinity)    .border(Color.red)    .padding(8)
Preview
100

When we specify a maxWidth value, the modifier takes the minimum of the proposed width and the specified value. This clamping behavior happens at two points: both when the frame proposes to its child, and when the frame reports its own size.

If we want to unconditionally accept the proposed width (regardless of the content's width), we can specify a minimum width of 0 and a maximum width of .infinity. Note that the red border now always stays within bounds. This behavior is documented.

Code
Text("Hello, world")    .fixedSize()    .frame(minWidth: 0, maxWidth: .infinity)    .border(Color.red)    .padding(8)
Preview
100

In the following example, our goal is to have a square image that fits inside any proposed size, regardless of the image's underlying aspect ratio. We start with an aspect ratio of 1 and fit. This means that a square rectangle will always be proposed to its child, the clipped modifier. The frame then unconditionally accepts the proposed size. The aspect ratio with the resizable image will use the image's size to fill the proposed size. The image draws out of bounds, but the clipped modifier takes care of clipping the image to exactly the square aspect ratio.

Code
Preview
300
Tree