Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alignment changes after adding a GeometryReader

Tags:

ios

swift

swiftui

I had a layout that essentially looked like this:

ZStack(alignment: .bottom) {
    GeometryReader { geometry in
        ZStack {
            Text("Centered")
        }
        .frame(width: geometry.size.width, height: geometry.size.height, alignment: .center)
        .background(Color.red)
    }
    Group {
        GeometryReader { geometry in // This GeometryReader is causing issues.
            VStack {
                Text("I want this at the bottom")
            }
            .frame(width: geometry.size.width, height: nil, alignment: .topLeading)
        }
    }
}

When this is rendered, both Text elements are rendered in the center of the screen. The second text element's container takes up the entire width of the screen, which is intended. If I remove the problematic GeometryReader, then the text is properly rendered at the bottom of the screen, but obviously the frame is not set to the entire width of the screen. Why is this happening?

like image 323
Max Avatar asked Jan 26 '20 03:01

Max


Video Answer


2 Answers

By default SwiftUI containers tight to content, but GeometryReader consumes maximum of available space. So if to remove second GeometryReader the VStack just wraps internal Text.

If it is still needed to keep second GeometryReader (to read width) and put text to the bottom, the simplest approach would be to add Spacer as below

enter image description here

Group {
    GeometryReader { geometry in
        VStack {
            Spacer()
            Text("I want this at the bottom")
        }
        .frame(width: geometry.size.width, height: nil, alignment: .topLeading)
    }
}

Alternate approach of how to stick view at bottom you can find in my answer in this post Position view bottom without using a spacer

like image 179
Asperi Avatar answered Sep 30 '22 17:09

Asperi


How about this?

struct ContentView: View {
    var body: some View {
        ZStack(alignment: .bottom) {
            GeometryReader {geometry in
                Text("Centered")
                    .frame(width: geometry.size.width, height: geometry.size.height)
                    .background(Color.red)
            }
            WidthReader {w in
                Text("I want this at the bottom").frame(width: w)
            }
        }
    }
}

struct WidthReader<Content: View>: View {
    let widthContent: (CGFloat) -> Content
    @State private var width: CGFloat = 0
    @State private var height: CGFloat = 0
    var body: some View {
        GeometryReader {g in
            widthContent(width).background(
                GeometryReader {g1 in
                    Spacer().onAppear {height = g1.size.height}.onChange(of: g1.size.height, perform: {height = $0})
                }
            ).onAppear {width = g.size.width}.onChange(of: g.size.width, perform: {width = $0})
        }.frame(height: height)
    }
}
like image 31
蘇哲聖 Avatar answered Sep 30 '22 17:09

蘇哲聖