John Sorensen
John Sorensen

Reputation: 960

Swift UI adjust VStack children height to content height

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()
            }
        }
    }
}

Upvotes: 0

Views: 2340

Answers (1)

jnpdx
jnpdx

Reputation: 52505

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()
            }
    }
}

Upvotes: 2

Related Questions