🔥

Fixing Inconsistent Spacing in SwiftUI Stacks

TL;DR: The default spacing (spacing: nil) in a VStack or HStack is not a fixed value. Instead, it is dynamically calculated based on the types of adjacent views (e.g., Text vs. Button). To ensure uniform spacing, you must explicitly set the spacing parameter (e.g., spacing: 10 or spacing: 0).

The Problem

In SwiftUI layout containers (such as VStack, HStack, or Grid), developers often notice that the gaps between child views appear inconsistent. Even without any padding applied, some views seem to “huddle closer” together, while others stand further apart.

The Cause

This phenomenon is not a bug; it is an adaptive layout logic designed by SwiftUI to comply with Apple’s Human Interface Guidelines (HIG):

  1. Dynamic Calculation (Magic Spacing): When a container’s spacing parameter is nil (the default), SwiftUI does not use a fixed pixel value (like 8pt).
  2. Context Awareness: The system analyzes the types of adjacent views. For example, the default gap between two Text views is usually smaller than the gap between a Text view and a Button.
  3. Platform Differences: This calculation logic behaves differently on iOS, macOS, and watchOS to adapt to the visual specifications of each platform.

The Solution

If you aim for pixel-perfect precision or want absolutely uniform spacing between all child views, you must explicitly specify the spacing parameter.

1. Uniform Fixed Spacing

To force a consistent 10pt distance between all child views:

Swift
VStack(spacing: 10) {
    Rectangle().frame(height: 50)
    Text("Fatbobman")
    Rectangle().frame(height: 50)
}

2. Eliminating All Spacing

This is the most common requirement, especially when you need to stitch views together seamlessly:

Swift
// Equivalent to spacing: 0
HStack(spacing: 0) {
    Color.red
    Color.blue
}

When to Use the Default (nil)?

While default behavior might lead to visual inconsistency, using the default (nil) is beneficial when building Forms, information display pages, or standard system-style interfaces. It ensures your App looks native and automatically maintains visual balance across different Dynamic Type sizes.

Further Reading

Related Tips

Subscribe to Fatbobman

Weekly Swift & SwiftUI highlights. Join developers.

Subscribe Now