3

I just started with SwiftUI, and seems VStack and HStack is very similar as flex box in web. On the web, it's easy to split two sub views as height weight with flex

<div id="parent" style="display: flex; flex-direction: column; height: 300px">
  <div id="subA" style="flex: 1; background-color: red">Subview A</div>
  <div id="subB" style="flex: 2; background-color: yellow">Subview B</div>
</div>

enter image description here

I wonder if it's possible on swiftUI too.

VStack {
    VStack {
        Text("Subview A")
    } // Subview A with height 100
        .background(Color.red)
    VStack {
        Text("Subview B")
    } // Subview B with height 200
        .background(Color.yellow)
}
    .frame(height: 300, alignment: .center)

How can I implement that?

Nimantha
  • 6,405
  • 6
  • 28
  • 69
rener172846
  • 431
  • 1
  • 6
  • 19

1 Answers1

3

UPDATE #2:

Thanks to this answer and code from @kontiki, here's what easily works instead of using this deprecated method:

Declare this:

@State private var rect: CGRect = CGRect()

Then create this:

struct GeometryGetter: View {
    @Binding var rect: CGRect

    var body: some View {
        GeometryReader { geometry in
            Group { () -> ShapeView<Rectangle, Color> in
                DispatchQueue.main.async {
                    self.rect = geometry.frame(in: .global)
                }
                return Rectangle().fill(Color.clear)
            }
        }
    }
}

(For those familiar with UIKit, you are basically creating an invisible CALayer or UIView in the parent and passing it's frame to the subview - apologies for not being 100% technically accurate, but remember, this is not a UIKit stack in any way.)

Now that you have the parent frame, you can use it as a base for a percentage - or "relative" - of it. In this question there's a nested VStack inside another and you want the lower Text to be twice the vertical size of the top one. In the case of this answer, adjust your `ContentView to this:

struct ContentView : View {
    @State private var rect: CGRect = CGRect()
    var body: some View {
        VStack (spacing: 0) {
            RedView().background(Color.red)
                .frame(height: rect.height * 0.25)
            YellowView()
        }
        .background(GeometryGetter(rect: $rect))
    }
}

UPDATE #1:

As of beta 4, this method is deprecated. relativeHeight, relativeWidth, relativeSizehave all been deprecated. Useframeinstead. If you want *relatve* sizing based on aView's parent, use GeometryReader` instead. (See this question.)


ORIGINAL POST:

Here's what you want. Keep in mind that without modifiers, everything is centered. Also, relativeHeight seems (at least to some) not very intuitive. The key is to remember that in a VSTack the parent is 50% of the screen, so 50% of 50% is actually 25%.

Alternatively, you can dictate frame heights (letting the width take up the whole screen). but your example suggests you want the red view to be 25% of the entire screen no matter what the actual screen size is.

struct RedView: View {
    var body: some View {
        ZStack {
            Color.red
            Text("Subview A")
        }
    }
}
struct YellowView: View {
    var body: some View {
        ZStack {
            Color.yellow
            Text("Subview B")
        }
    }
}
struct ContentView : View {
    var body: some View {
        VStack (spacing: 0) {
            RedView().background(Color.red).relativeHeight(0.50)
            YellowView()
        }
    }
}

enter image description here

Nimantha
  • 6,405
  • 6
  • 28
  • 69
  • So do you mean it's impossible with VStack as subviews? – rener172846 Jul 04 '19 at 20:19
  • i'm unsure what you mean. My `ContentView` *is* a ` VStack`. –  Jul 04 '19 at 20:24
  • Ah, never mind. Sure, this is a "base". There's no reason the `RedView` and `YellowView` couldn't contain a `VStack` instead of a plain `Text`. At runtime `SwiftUI will compress them into a single `View`. The **real** piece is understanding where (and in what order) to place your modifiers. –  Jul 04 '19 at 20:28
  • I mean your `RedView` and `YellowView` are based on ZStack, but sometimes I need to use `VStack` as the subview which contains several Texts and Buttons in it. The above example with only one `Text` is just only the example – rener172846 Jul 04 '19 at 20:44
  • Also I tried to apply `relativeHeight(0.50)` to the `VStack` but the result is so unexpected as you mentioned like `50% of 50%`. Is that the issue which Apple still have to fix? – rener172846 Jul 04 '19 at 20:48
  • I used a `ZStack` in order to apply the background color correctly. (If you use a `background` modifier in other places it doesn't work like you want.) As for the behavior of `relativeHeight`? I'm not so sure it *isn't* how Apple intends. It makes "some" logical sense, at least to me. But yeah, remembering it's only beta 3 with about 2 months to go, this would be one thing I'd note and test in every beta release going forward. (As for reporting it as a bug? I'm not sure.) Bottom line is that if you want to achieve that result that **is** so easy to do in HTML, you least have one. ;-) –  Jul 04 '19 at 21:14