andrewz
andrewz

Reputation: 5220

SwiftUI erroneous vertical alignment of overlay when using GeometryReader

I have an overlay view that I want to appear hugging the top edge of its parent view (aligned vertically to top not center, see snapshot B) and have the ability to semi-hide it when tapped (move it up so that it doesn't obscure its parent view, and yet have a portion of it visible, say 64 pixels so that it can still be tapped back into original position, see snapshot C). Problem is that when I use the GeometryReader to get the height of the overlay view I'm trying to move, the overlay appears in the center of the view originally (see snapshot A). And if I don't use the geometry reader I don't know by how much to offset the overlay view:

    @State var moveOverlay = false

    var body : some View {

        VStack {
            ForEach(0...18, id: \.self) { _ in
                Text("This is the background... This is the background... This is the background... This is the background... ")
            }
        }
        .overlay(VStack {
            GeometryReader { proxy in
                Text("This is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\n")
                    .lineLimit(9)
                    .background(Color.gray)
                    .padding(32)
                    .frame(maxWidth: nil)
                    .offset(x: 0, y: self.moveOverlay ? -proxy.size.height + 128 + 64 : 0)
                    .onTapGesture {
                        self.moveOverlay = !self.moveOverlay
                }
            }
            Spacer()
        })
    }

Not sure why geometry reader has this effect on the layout.

(A) This is the way it appears with geometry reader code:

enter image description here

(B) If I remove the geometry reader code I get the desired effect:

enter image description here

(C) And this is the way I want the overlay when moved to the top:

enter image description here

EDIT: The code displayed above produces the layout (A). I want layout (B) which I get if I omit the geometry reader code.

Upvotes: 4

Views: 4315

Answers (2)

Fabian
Fabian

Reputation: 5348

You can use the second argument in func overlay<Overlay>(_ overlay: Overlay, alignment: Alignment = .center) to achieve it by aligning it at the top end and moving it up or down with offset depending on the mode.

struct AlignmentView: View {
    @State var hideOverlay = false

    var body : some View {
        GeometryReader{ proxy in
            VStack {
                ForEach(0...18, id: \.self) { _ in
                    Text("This is the background... This is the background... This is the background... This is the background... ")
                }
            }
            .overlay(
                Text("This is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\n")
                        .lineLimit(9)
                        .background(Color.gray)
                        .frame(maxWidth: nil)
                        .offset(x: 0, y: self.hideOverlay ? -proxy.size.height / 5.0 : 50)
                        .onTapGesture {
                            withAnimation {
                                self.hideOverlay.toggle()
                            }
            },
                alignment: .top)
            }
    }
}

Upvotes: 1

Asperi
Asperi

Reputation: 257493

Well... still not sure but here is some approach

Tested with Xcode 11.4 / iOS 13.4

demo

.overlay(
    ZStack(alignment: .top) {
        Color.clear
        Text("This is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\nThis is the overlay text snippet sentence that is multiline...\n")
                .lineLimit(9)
                .background(Color.gray)
                .alignmentGuide(.top) { self.hideOverlay ? $0.height * 3/2 : $0[.top] - 64 }
                .onTapGesture {
                    withAnimation(Animation.spring()) {
                        self.hideOverlay.toggle()
                    }
            }
    })

Upvotes: 4

Related Questions