2

I would like to stop GeometryReader from interfering with my layout in a VStack but I'm not sure what the correct approach is.

Given the example of a simple graph with a title and caption:

struct Graph: View {
  var body: some View {
    HStack(spacing: 0) {
      Color.red.frame(width: 100, height: 80)
      Color.blue.frame(width: 100, height: 120)
      Color.green.frame(width: 100, height: 180)
    }
  }
}

struct GraphExample: View {
  var body: some View {
    VStack {

      Text("My graph")

      // Graph represents a custom view with a natural and non-static height
      Graph()

      Text("My graph caption")
    }
  }
}

Produces this expected result:

enter image description here

But if we update the Graph view to use a GeometryReader to evenly split the graph across the width of the screen the layout changes.

struct Graph: View {
  var body: some View {
    GeometryReader { proxy in
      HStack(spacing: 0) {
        Color.red.frame(width: proxy.size.width / 3, height: 80)
        Color.blue.frame(width: proxy.size.width / 3, height: 120)
        Color.green.frame(width: proxy.size.width / 3, height: 180)
      }
    }
  }
}

This makes the Graph view fill all available space in VStack

enter image description here

Is there a way to allow Graph view to just fill its natural height up, and not consume all available height while still using the GeometryReader.

Bear in mind that Graph could be any view here where the exact size is not known, so setting a static size is not possible. i.e. without GeometryReader, Graph can takes up only the needed amount of vertical space in the VStack.

Craigt
  • 3,418
  • 6
  • 40
  • 56
  • Do you want to make a bar chart? that takes dynamic bar height based on the data value? – Md. Yamin Mollah May 06 '20 at 10:51
  • `GeometryReader` is greedy with respect to layout - it always attempts to maximize its frame inside of the context of its parent. In this case, I recommend simply adding horizontal padding to your geometry reader to define a distance from the edges of the screen. e.g. `.padding(.horizontal, 20)` – Carter Foughty Apr 30 '22 at 05:24

1 Answers1

1

Here is possible solution. Tested with Xcode 11.4 / iOS 13.4

enter image description here

struct Graph: View {
  @State private var width = UIScreen.main.bounds.width // just initial constant
  var body: some View {
      HStack(spacing: 0) {
        Color.red.frame(width: width / 3, height: 80)
        Color.blue.frame(width: width / 3, height: 120)
        Color.green.frame(width: width / 3, height: 180)
      }.background(GeometryReader { gp -> Color in
        let frame = gp.frame(in: .local)
        DispatchQueue.main.async {
            self.width = frame.size.width // << dynamic, on layout !!
        }
        return Color.clear
      })
  }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • 1
    Looks like a crazy trick probably leading to layout loops. Especially the `DispatchQueue.main.async` part. – kelin Apr 29 '21 at 08:45