Antony Stubbs
Antony Stubbs

Reputation: 13585

GeometryReader inside a ScrollView collapses the GeometryReader, regardless of child sizes

I'm trying to make a sub view which contains a drawing which I want the size to be proportional to a fraction of the parent container.

This works fine except when put inside a scroll view somewhere up the hierarchy.

As seen from the screenshots when inside of the stack the loudest as expected. But when you put inside a scroll view the size of the geometry reader collapses to near zero and its children overflow its boundaries. As though it's rendering its children on a different Z index. In fact if you remove the wrapping VStack from the geometry render contents it doesn't fact layout all its children as though it were wrapped in a ZStack.

EDIT: To be clear: the ScrollView is not owned by the component. The component should not be aware if it's in a ScrollView or not - it's just a poor helpless component, being thrown around. That's why I put the ScrollView into the preview code, not the component.

I've tried all sorts of combinations of fixed sizes and men and mags and ideal. The only work around I could find was this hacky solution - the edited part of the question near the bottom where the width reported by the geometry reader is captured through a workaround into a state variable and then re-used to set the frame size of a sibling view.

Note, I'm a complete beginner. There seems to be some interaction between the scroll view and the geometry reader that is beyond my current understanding.

Seems to be a confusing topic.

import SwiftUI

struct ScrollViewGeoReader: View {
    var body: some View {
        GeometryReader{ g in
            VStack{
                let width = g.size.width
                Circle().frame(width: width/3, height: width/3, alignment: .center)
                Text("inside geo")
                Text("inside geo")
                Text("inside geo \(width)")
                Text("inside geo")
                Text("inside geo")
            }
            .border(Color.green, width: 2)
        }
        .border(Color.red, width: 3)
    }
}

struct ScrollViewGeoReader_Previews: PreviewProvider {
    static var previews: some View {
        VStack {
//            VStack {
            ScrollView {
                ScrollViewGeoReader()
                Text("Next scrollview item")
            }
            .border(Color.blue, width: 2)

        }
    }
}

ZStack (expected layout):

ZStack (expected layout)

ScrollView (see how the red frame of the geometry read it has collapsed to size 10):

enter image description here

EDIT:

Also note that the same problem occurs with or without the circle, which is also a problem I have. So it's not chicken / egg as far as the width capturing is concerned, at least in terms of Circles. I would have though the Text components know their own size, and would tell the GR.

enter image description here

vs

enter image description here

Upvotes: 11

Views: 3167

Answers (1)

Asperi
Asperi

Reputation: 258345

Here is scratchy (w/o subviews separation) possible solution for layout. If/when internal subviews separated the geometry width can be injected by constructor arguments.

Tested with Xcode 12.

demo

struct TestScrollViewWithGeometry: View {
    var body: some View {
        GeometryReader{ g in
            ScrollView {
                VStack{
                    let width = g.size.width
                    Circle().frame(width: width/3, height: width/3, alignment: .center)
                    Text("inside geo")
                    Text("inside geo")
                    Text("inside geo \(width)")
                    Text("inside geo")
                    Text("inside geo")
                }
                .border(Color.green, width: 2)
                Text("Next scrollview item")
            }
            .border(Color.blue, width: 2)
        }
        .border(Color.red, width: 3)
    }
}

Upvotes: 0

Related Questions