David Yang

Tips and posts for iOS developers from an iOS developer.

Early on, SwiftUI introduced Spacer. But did you know that you can replace it with something else in most use cases?

According to Apple’s documentation, a Spacer is:

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 let’s start a simple HStack.

struct ContentView: View {
    var body: some View {
        HStack {
            Image(systemName: "checkmark")
            Text("Taylor")
        }
        .border(.blue)
        .padding()
    }
}

no-spacer

Spacer

From there we can add a leading space.

struct ContentView: View {
    var body: some View {
        HStack {
            Spacer()
            Image(systemName: "checkmark")
            Text("Taylor")
        }
        .border(.blue)
        .padding()
    }
}

leading-spacer

Or we can have a trailing space.

struct ContentView: View {
    var body: some View {
        HStack {
            Image(systemName: "checkmark")
            Text("Taylor")
            Spacer()
        }
        .border(.blue)
        .padding()
    }
}

trailing-spacer

Or have a space between the Image and the Text.

struct ContentView: View {
    var body: some View {
        HStack {
            Image(systemName: "checkmark")
            Spacer()
            Text("Taylor")
        }
        .border(.blue)
        .padding()
    }
}

middle-spacer

But we can actually achieve those same behaviors with the .frame(maxWidth:maxHeight:alignment:) modifier.

Using the frame modifier

By setting maxWidth to .infinity, we’re indicating that the container view should use as much horizontal space as it can.

The .trailing alignment will make sure the content is aligned to the trailing end.

struct ContentView: View {
    var body: some View {
        HStack {
            Image(systemName: "checkmark")
            Text("Taylor")
        }
        .border(.blue)
        .padding()
        .frame(maxWidth: .infinity, alignment: .trailing)
    }
}

trailing-alignment

You can also choose to use a .leading alignment.

struct ContentView: View {
    var body: some View {
        HStack {
            Image(systemName: "checkmark")
            Text("Taylor")
        }
        .border(.blue)
        .padding()
        .frame(maxWidth: .infinity, alignment: .leading)
    }
}

leading-alignment

And if you want to add a space between the Image and the Text, you can just use the frame modifier on one of them and set the desired alignment.

struct ContentView: View {
    var body: some View {
        HStack {
            Image(systemName: "checkmark")
            Text("Taylor")
                .frame(maxWidth: .infinity, alignment: .trailing)
        }
        .border(.blue)
        .padding()
    }
}

text-railing-alignment

The difference

Visually, both approach will look exactly the same (if you forget about the blue border which is only here to understand the behavior of all of this). But now, you’re setting the behavior directly on the container instead of adding an extra and flexible component inside of it.

As a consequence, using a .frame modifier will save you a lot in performance over a Spacer, especially in lists or long horizontal/vertical stacks.

Conclusion

Of course Spacer can still be useful in more complex components. But my advice would be to try to use Spacer as less as you can for performance costs and readability.

I’m also only working with HStack here in my code samples, but dealing with VStack is mostly the same with the maxHeight parameter and some .top or .bottom alignment.

For more complex components, you can also combine horizontal and vertical alignment with .topLeading, .topTrailing, .bottomLeading, .bottomTrailing or .center alignment.