Search code examples
swiftuilayoutviewstack

ZStack inside GeometryReader causing layout issues


I'm having issues in using GeometryReader and ZStack. This is the code of my view:

var body: some View {
    ZStack {
        RoundedRectangle(cornerRadius: 20)
            .fill(color.gradient.shadow(.drop(radius: 6)))
        Text(title)
            .foregroundColor(isBackgroundDark ? .white : .black)
    }
    .frame(height: 150)
    .aspectRatio(contentMode: .fill)
}

This renders correctly as this:

enter image description here

If I put the code inside a GeometryReader, to let the height of the view be proportional to the width, this happens (code, then image):

var body: some View {
    GeometryReader { metrics in
        ZStack {
            RoundedRectangle(cornerRadius: metrics.size.width * 0.1)
                .fill(color.gradient.shadow(.drop(radius: 6)))
            Text(title)
                .foregroundColor(isBackgroundDark ? .white : .black)
        }
        .frame(height: metrics.size.width / 3)
        .aspectRatio(contentMode: .fill)
    }
}

enter image description here

If I set the red background on the ZStack inside the GeometryReader, this is what I see.

enter image description here

How can I solve this?


Solution

  • There are a couple things happening that are making your view look different then what you'd expect.

    • Unlike ZStack, GeometryReader does not center it's children.
    • While ZStack can center it's children, you've limited it's height so it cannot fill the view.

    With those facts in mind, below are two solutions based around overcoming one of those two conditions listed above.

    1: Use Position

    Since GeometryReader doesn't automatically center it's children, you'll have to do that yourself using the position modifier.

    .position(x: metrics.frame(in: .local).midX, y: metrics.frame(in: .local).midY)
    

    Append the above modifier to your ZStack to center it within the parent GeometryReader.

    2: Use another Parent

    While GeometryReader can't center it's children, the Stacks (VStack, HStack, ZStack) can. Thus, you can center your content by placing your ZStack inside of a VStack whose frame fills the screen.

    GeometryReader { metrics in
        VStack {
            ZStack {
                RoundedRectangle(cornerRadius: metrics.size.width * 0.1)
                .fill(color.gradient.shadow(.drop(radius: 6)))
                Text(title)
                .foregroundColor(isBackgroundDark ? .white : .black)
            }
            .frame(height: metrics.size.width / 3, alignment: .center)
            .aspectRatio(contentMode: .fill)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    } 
    

    Again, you could use HStack here, or even another ZStack, if you prefer.