How to use frames instead of spacers in SwiftUI

April 19, 2021 • 4 min read

We all enjoy the beauty and simplicity of using a Spacer to make our SwiftUI Views look like they came straight out of Dribbble. If you didn’t know before, you achieve the same results using built-in frames with SwiftUI. Let’s take a quick look at how we can simplify our code by using fewer Views and more View Modifiers.

Spacer

If you look around at some Open Source SwiftUI projects, you might often see them using a Spacer to fill the remaining size of a view. This technique is from a lack of knowledge of how you can use built-in frames to achieve the same result.

Before we dive into the code, let’s see what the Apple documentation says about the Spacer

A flexible space that expands along the major axis of its containing stack layout, or on both axes if not contained in a stack.

So, in other words, our Spacer wants to take up as much space as possible. So if it’s in a VStack it will grow vertically, and if it’s in an HStack it will grow horizontally. Easy enough.

Let’s see this in a basic example:

struct TestingFrames: View {
    var body: some View {
        HStack {
            Spacer()
            Text("Centered")
            Spacer()
        }
    }
}

The intended effect here is to fill up the whole width of the View that the HStack is inside.

Once you add some background colors and shadows, that’s pretty much ready to ship! Let me say that there is nothing wrong with this approach. SwiftUI has many ways of solving similar issues, and if you find a solution that works for you, then that’s the most important thing!

Keeping our goal of expansion in mind, would you introduce more views into your project by adding Spacers, or would you modify the frame of your Text View?

Frame Modifier

The frame modifier can be applied to any View in SwiftUI and is very powerful out-of-the-box. Here is what Apple says about the frame view modifier.

Positions this view within an invisible frame.

Well, that’s not very helpful. If you kept scrolling, you would notice some documentation that will soon become your best friend:

func frame(width: CGFloat? = nil, height: CGFloat? = nil, alignment: Alignment = .center)

With a frame we can give our views an optional width, height, and alignment. Optionally, you can also include some other values by intilizing it this way:

func frame(minWidth: CGFloat? = nil, idealWidth: CGFloat? = nil, maxWidth: CGFloat? = nil, minHeight: CGFloat? = nil, idealHeight: CGFloat? = nil, maxHeight: CGFloat? = nil, alignment: Alignment = .center)

We can tell our View the minimum size (if we care for that), the maximum size (if we care for that), and even the ideal size (if we care about that). This process is powerful and will make you quickly want to stop using Spacers. To reproduce the same design we saw above, we can create our Text View and apply a frame like so:

struct TestingFrames: View {
    var body: some View {
        Text("Centered")
            .frame(maxWidth: .infinity)
    }
}

Our “Centered” text is now naturally centered and expanding from edge to edge. This technique reduced the amount of code we had to write and opened up another door. What if we know that the Text should take up 300 pixels, but no more than that. Well, if you like living on the edge and want to add hard-coded values into your app, then you’ll want to use a fixed frame for the View. Something like this:

Text("Centered").frame(width: 100)

Optionally, you can opt-in for the minWidth flexible frame method and give your text this modifier: Text("Centered View is Centered").frame(minWidth: 100). Your View will not be any smaller than 100 pixels; however, it allows its bounds to grow as more content is added into the View. Now our app is more responsive.

Conclusion

Like I mentioned previously, there is nothing wrong with using Spacers in your application. If Apple didn’t want you to have them, they would have never created the View. I propose a fresh way of looking at your code structure and a new view into how you can use the DSL of SwiftUI to make concise and expressive code without having to have your files bloated by Spacer calls.