swiftuigeometryreader

SwiftUI onGeometryChange modifier with frame


What's wrong with the following code that the viewSize height remains zero despite having a frame modifier set to height 60?


struct TestGeometryModifierView: View {
    @State var viewSize:CGSize = .zero
    
    var body: some View {
        Color.red
            .onGeometryChange(for: CGSize.self) { proxy in
                proxy.size
            } action: { newValue in
                viewSize = newValue
                print("View Size \(newValue)")
            }
            .frame(height:viewSize.height)
    }
    
}

#Preview {
    TestGeometryModifierView()
        .frame(height:60)
}

The print statement on console shows only one line. I expect it to change when frame height is set on the view.

View Size (402.0, 0.0)

Solution

  • A Color is greedy and consumes as much space as possible. However, you are also setting the height using .frame and this height is initialized to zero. So on initial show, the width is as wide as possible, but the height is zero.

    The height you are measuring using .onGeometryChange is therefore zero, in other words, the value that the state variable was initialized with. Since the size does not change, .onGeometryChange is not triggered again.

    The height of 60 that you are setting on the surrounding view has no impact on the view that you are measuring with .onGeometryChange. So this additional .frame modifier does not cause .onGeometryChange to trigger.


    If you are trying to measure the full size available then you could use an optional for the state variable instead:

    @State var viewSize: CGSize?
    
    Color.red
        .onGeometryChange(for: CGSize.self) { proxy in
            // ... as before
        }
        .frame(height: viewSize?.height)
    

    The modifier .onGeometryChange is still only triggered once because the initial size is the same as the size that is set by the .frame modifier. But now, the state variable measures a useful value:

    View Size (393.0, 60.0)