Why is the default spacing in SwiftUI stacks non-zero?

I've encountered unintuitive behavior of SwiftUI stacks. When I don't specify spacing on stacks I expect there to be none, but there is. The Apple documentation says that the spacing is adaptive and is calculated automatically. This seems weird to me. Usually, I need a specific spacing, very often zero spacing, and rarely a range. But I can't imagine situations where I need unknown spacing. Apple developers thought it was a good idea to make this the default behavior, why? I'm really interested in what arguments they were guided by and maybe there is a way to set it up.
I can’t say that this is a serious problem for me, but I often encountered layout bugs due to forgotten spacing: 0 and watched other developers who are unfamiliar with this behavior spend a lot of time debugging the layout, and without help from outside they cannot guess that the lack of spacing in the code can mean something other than zero.
PS: this behavior can be useful when I type "by eye" without design, but this is rare

1 Like

The default spacing should resolve to the recommended spacing for the particular controls, context and platform in use. Thus it's supposed to respect the human interface guidelines and be what most developers typically want to use.

3 Likes

This is a discussion topic for designers, it may be useful to make a plug-in for Figma that sets spacings in accordance with the human interface guidelines, but SwiftUI is a tool not for designers, but for developers, we do not come up with a design, but implement it and cannot set unknown spacings

1 Like

The visual look and style of system controls may change between OS versions and the default spacing may change accordingly. This is only made possible because the default spacing is not communicated explicitly.

1 Like

Furthermore this allows developers to target several platforms with a single codebase.

4 Likes

This argument really explains Apple developers logic to me, thanks
But I still fundamentally disagree with the fact that such behavior should be default and implicit for all stacks in the code. Inside the system components it is quite appropriate, but there are no more inside my components. Maybe it was worth doing something like HStack(spacing: .adaptive), and some environment that defines the behavior view.adaptiveSpacingBehavior(.custom).
Stacks are used everywhere, they are the basic layout element in SwiftUI

I suspect it would still be possible for Apple to add an API that would allow customization of the default spacing and which you could use on the root of your view hierarchy. You can make a case for it via feedback assistant.

I'm sorry, but this is an assumption with no backing to support it. H/VStack is used in so many different places, you simply cannot assume that there's a default spacing developers will need. 0 would have been smarter/better in my opinion (also would have avoided many issues beginners have with SwiftUI).
The answer to the question above is, plainly, poor API design choices.

3 Likes

If SwiftUI default behaviour is fundamentally against your values you can easily make your own view with the required behaviour:

import SwiftUI

struct VerticalStack<Content: View> : View {
    var alignment: HorizontalAlignment
    var spacing: Optional<CGFloat>
    @ViewBuilder var content: () -> Content
    
    enum Spacing { case adaptive }
    
    init(alignment: HorizontalAlignment = .center, spacing: Spacing, content: @escaping () -> Content) {
        self.alignment = alignment
        self.spacing = nil
        self.content = content
    }
    
    init(alignment: HorizontalAlignment = .center, spacing: CGFloat = 0, content: @escaping () -> Content) {
        self.alignment = alignment
        self.spacing = spacing
        self.content = content
    }
    
    var body: some View {
        VStack(alignment: alignment, spacing: spacing, content: content)
    }
}

struct ContentView: View {
    var body: some View {
        VStack {}
        VerticalStack(spacing: .adaptive) { // "system chosen" spacing
            Text("xx")
        }
        VerticalStack { // no spacing
            Text("xx")
        }
        VerticalStack(spacing: 123) { // explicit spacing
            Text("xx")
        }
    }
}

VerticalStack(spacing: .adaptive) will choose system spacing, VerticalStack() will mean zero spacing (or you can make it a syntax error) and VerticalStack(spacing: 123) will choose exact spacing.

5 Likes

Note that neither the current nil way of specifying default spacing, nor the hypothetical new adaptive enumeration constant as shown – are flexible enough if you want to do just a little bit more with the value:

VStack(spacing: .adaptive * 2)  // 🤔
VStack(spacing: .adaptive + 10) // 🤔

It'd work if it was a real value.

Consider this consideration in the API's you are creating.

1 Like

You could also set yourself how the .adaptive should be calculated, while the current nil is a black box

I tried to use auto padding / spacing in past but sometimes I have different behaviors in different iOS releases, also very bad or unexpected behaviors like have a list of buttons without spacing at all.

Use VStack a lot and many times like VStack(spacing: 0).

1 Like

The min size of Spacer is also non-zero. I think you need to adjust your thinking: a lot of values have non-zero default and you should allow SwiftUI do the cooking. You just ask it for a dish you want and how you want and SwiftUI will get the right recipe and “cook” the result you want. Leave as much as you can to the “default”. They all have sensible default values and most time they are non-zero.

The problem is that I already have the recipe: I have a design, I just need to describe it with a minimum amount of code.
It seems to me that different opinions on this matter are connected with personal experience.
If you make projects relying on native macos/ios design, then you have no problem with unknown spacing.
But most applications in the world rely on their own design system and all spacings are set to specific values.
Here are many examples of typical iOS app designs:

It is unlikely that you will find at least one design that can be accurately implemented relying on defaults.
I suspect that among the SwiftUI developers there are a lot of people with experience in developing native applications for Apple, so they made it as convenient as possible for themselves.