0

I have a VStack with three children. Each of those children have an HStack with some text and an empty space on the left. Here is a screenshot of the preview with a highlighted border around a VStack child, and one with a highlighted border around each HStack.

VStack View HStack View

How do I set each VStack child to shrink to the height of the HStack and get rid of the empty space on the bottom? Right now the problem seems to be caused by the VStack by default filling the entire screen and setting each child to equal height.

Current Code:

import Foundation
import SwiftUI


struct NameAndConnection: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text(data.name)
               .font(.headline)
               .fontWeight(.semibold)
               .foregroundColor(Color.black)
               .frame(alignment: .leading)
   
            Text(data.connection)
               .font(.callout)
               .fontWeight(.medium)
               .foregroundColor(Color.gray)
               .frame(alignment: .leading)
        }
    }
}

struct Description: View {
    var body: some View {
        Text(data.description)
    }
}

struct Post: View {
    var body: some View {
        GeometryReader { geo in
            HStack(alignment: .top, spacing: 0) {
                Spacer()
                VStack(alignment: .leading) {
                    NameAndConnection()
                    Description()
                        .padding(.top, 1.0)
                }
                .frame(width: geo.size.width * 0.75)
            }
        }
    }
}



struct PostView: View {
    var body: some View {
        GeometryReader { geo in
            VStack() {
                Post()
                Post()
                Post()
            }
        }
    }
}
John Sorensen
  • 710
  • 6
  • 29
  • How about adding a `frame()`? – Taeeun Kim Jul 24 '21 at 20:58
  • how would you get the height info from the HStack to the VStack frame()? – John Sorensen Jul 24 '21 at 21:05
  • Add your current code. It is somewhat useless to try to debug without it. Likely you just have to add a `Spacer()` below the lowest one. – jnpdx Jul 24 '21 at 21:05
  • Current code added. Would adding a Spacer force the VStack to no longer distribute space equally to the children and instead let children's HStack's control space? – John Sorensen Jul 24 '21 at 21:14
  • Please add code, not pictures of code, which are not copy/pastable, searchable, etc. Make sure it's a [mre] -- what you've posted so far can't actually be used by anyone because it relies on custom types. – jnpdx Jul 24 '21 at 21:19
  • There's not an easy way for me to copy / paste code unfortunately, as I am using a virtual mac and the copy / paste is local to the virtualization. Screenshots are the only way I have found to get code / previews from the virtual mac/ – John Sorensen Jul 24 '21 at 21:23
  • Is there a web browser? Send it from there. Or, retype the code here. – jnpdx Jul 24 '21 at 21:25
  • @JohnSorensen .frame(height: geo.size.height / 6) for VStack – Taeeun Kim Jul 24 '21 at 21:27
  • @TaeeunKim that worked when called after each Post in the VStack – John Sorensen Jul 24 '21 at 21:35
  • @jnpdx yes there is I'll copy my code in from there – John Sorensen Jul 24 '21 at 21:35
  • @TaeeunKim the only problem with that solution is that the constant 6 depends on there only being 3 posts and each post being about a 6th of the size of the screen. How would I edit this to make it work if we let each post vary in size and also vary the number of posts accordingly? – John Sorensen Jul 24 '21 at 21:46
  • The problem isn't the `VStack`s, it's the `GeometryReader`s, which take up all available space. Do you need to have the `geo.size.width * 0.75`, or are you willing to give that up for a different type of padding (a constant, for example)? – jnpdx Jul 24 '21 at 21:51
  • I do want the empty space to be a proportion of the available screen width. I might be willing the change the logic behind the empty space such that it isn't defined for each post but at the VStack level, if that makes sense. – John Sorensen Jul 24 '21 at 21:55

1 Answers1

2

GeometryReader will take up all available space. In general, it's widely considered good practice to try to use GeometryReader sparingly and come up with other more flexible solutions for different screen sizes.

When you do have to use it, often it's helpful to measure the background of a View -- that way, the View itself won't grow as it would if the GeometryReader were in the foreground. I've borrowed the GeometryReader from: https://stackoverflow.com/a/59733037/560942

After you've removed the GeometryReaders, you can use a Spacer to push the other VStacks to the top of the screen.

struct GeometryGetter: View {
    @Binding var rect: CGRect

    var body: some View {
        GeometryReader { (g) -> Path in
            print("width: \(g.size.width), height: \(g.size.height)")
            DispatchQueue.main.async {
                self.rect = g.frame(in: .global)
            }
            return Path() // could be some other dummy view
        }
    }
}

struct Post: View {
    @State private var rect1: CGRect = CGRect()
    
    var body: some View {
        HStack(alignment: .top, spacing: 0) {
            Spacer()
            VStack(alignment: .leading) {
                NameAndConnection()
                Description()
                    .padding(.top, 1.0)
            }.frame(width: rect1.width * 0.75)
        }
        .background(GeometryGetter(rect: $rect1))
        .border(Color.green)
    }
}



struct ContentView: View {
    var body: some View {
            VStack() {
                Post()
                Post()
                Post()
                Spacer()
            }
    }
}
jnpdx
  • 45,847
  • 6
  • 64
  • 94