While I was on a hiatus of a few months from the blog, it looks like I am back to writing! Writing a short article like this one is something that I avoided for unknown reasons, but lately, I have realized that I am going back to the same small problems again and again and need to document them somewhere. And the best place? Personal blog!

And probably you may also face the same problems, so it is a win-win situation for both of us.

This article is about creating VStack and HStack with zero spacing so that you can give explicit spacing to the inner views. More often than not, the designer may provide you with views that have explicit spacing everywhere.

VStack and HStack already have a default spacing that differs across operating systems and it gets cumbersome using VStack(spacing: 0) { } or HStack(spacing: 0) { } across the views.

You can create a handy kind-of syntactic sugar view with zero spacing by default. Let’s name them VSStack and HSStack.

VSStack

The zero spacing vertical stack follows a similar structure compared to a VStack where you initialize it with the horizontal alignment of default value .center, and it accepts a view builder as the parameter:

struct VSStack<Content>: View where Content: View {
    private var content: Content
    private var alignment: HorizontalAlignment
    
    init(_ alignment: HorizontalAlignment = .center, @ViewBuilder content: () -> Content) {
        self.alignment = alignment
        self.content = content()
    }
    
    var body: some View {
        VStack(alignment: alignment, spacing: 0) {
            content
        }
    }
}

Usage:

VSStack { 
    Text(track.title)
        .font(.headline)
        .padding(.bottom, 8)
    
    Text(track.artistName)
        .foregroundColor(.secondary)
        .padding(.bottom, 4)
}

If you want to provide alignment:

VSStack(.leading) {
    Text(title)
        .font(type: .montserrat, weight: .regular, size: 14)
        .padding([.leading, .bottom], 4)

    Text(subtitle)
        .font(type: .montserrat, weight: .light, size: 12)
}

HSStack

The zero spacing horizontal stack follows a similar structure compared to an HStack where you initialize it with vertical alignment of default value .center, and it accepts a view builder as the parameter:

struct HSStack<Content>: View where Content : View {
    private var content: Content
    private var alignment: VerticalAlignment

    init(_ alignment: VerticalAlignment = .center, @ViewBuilder content: () -> Content) {
        self.alignment = alignment
        self.content = content()
    }
    
    var body: some View {
        HStack(alignment: alignment, spacing: 0) {
            content
        }
    }
}

Usage:

HSStack { 
    Text(track.title)
        .font(.headline)
        .padding([.leading, .bottom], 8)
        
    Spacer()
    
    Text(track.rating)
        .foregroundColor(.secondary)
        .padding(.trailing, 8)
        .padding(.bottom, 4)
}

If you want to provide alignment:

HSStack(.top) {
    WebImage(url: track.artwork.url)
        .resizable()
        .indicator(.activity)
        .padding(.leading, 4)

    Text(track.title)
        .font(type: .montserrat, weight: .bold, size: 14)
        
    Spacer()
}

Conclusion

These views are more of me being lazy, but I like them when working on a project with hundreds of them.

If you have a better approach, please tag @rudrankriyam on Twitter! I love constructive feedback and appreciate constructive criticism.

Thanks for reading, and I hope you’re enjoying it!