Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to size subviews in HStack by percentage without GeometryReader (SwiftUI)?

I've got a simple HStack with subviews inside. How can I tell the first subview to be 60% the size of the HStack without using a GeometryReader?

struct ContentView: View {
    var body: some View {
        HStack {
            Color.red.opacity(0.3)
            Color.brown.opacity(0.4)
            Color.yellow.opacity(0.6)
        }
    }
}

The code above makes each subview the same size. But I want the first one to be 60% regardless of it's content. In this example, it is a color, but it could be anything. The HStack is dynamic in size.

enter image description here

Edit: Why no GeometryReader?

When I want to place multiple of those HStacks inside a ScrollView, they overlap, because the GeometryReader's height is only 10 Point. As mentioned above, the Color views could be anything, so I used VStacks with cells in it that have dynamic heights.

struct ContentView: View {
    
    var body: some View {
        ScrollView(.vertical) {
            ProblematicView()
            ProblematicView()
        }
    }
}

struct ProblematicView: View {
    
    var body: some View {
        GeometryReader { geo in
            HStack(alignment: .top) {
                VStack {
                    Rectangle().frame(height: 20)
                    Rectangle().frame(height: 30)
                    Rectangle().frame(height: 20)
                    Rectangle().frame(height: 40)
                    Rectangle().frame(height: 20)
                }
                .foregroundColor(.red.opacity(0.3))
                .frame(width: geo.size.width * 0.6)
                .overlay(Text("60%").font(.largeTitle))
                
                VStack {
                    Rectangle().frame(height: 10)
                    Rectangle().frame(height: 30)
                    Rectangle().frame(height: 20)
                }
                .foregroundColor(.brown.opacity(0.4))
                .overlay(Text("20%").font(.largeTitle))
                
                VStack {
                    Rectangle().frame(height: 5)
                    Rectangle().frame(height: 10)
                    Rectangle().frame(height: 24)
                    Rectangle().frame(height: 10)
                    Rectangle().frame(height: 17)
                    Rectangle().frame(height: 13)
                    Rectangle().frame(height: 10)
                }
                .foregroundColor(.yellow.opacity(0.6))
                .overlay(Text("20%").font(.largeTitle))
            }
        }
        .border(.blue, width: 3.0)
    }
}

As you can see, the GeometryReader's frame is too small in height. It should be as high as the HStack. That causes the views to overlap.

enter image description here

like image 337
WalterBeiter Avatar asked Oct 17 '25 07:10

WalterBeiter


1 Answers

I don't know the exact reason (might be a bug in GeometryReader), but placing the GeometryReader outside the ScrollView, and passing down its width makes your code behave as you expect.

struct ContentView: View {
    var body: some View {
        GeometryReader { geo in
            ScrollView {
                ProblematicView(geoWidth: geo.size.width)
                ProblematicView(geoWidth: geo.size.width)
            }
        }
        .border(.blue, width: 3.0)
    }
}

struct ProblematicView: View {
    let geoWidth: CGFloat
    
    var body: some View {
        // same code, but using geoWidth to compute the relative width

Result:

like image 123
Cristik Avatar answered Oct 19 '25 22:10

Cristik



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!