3

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.

andrewz
  • 4,729
  • 5
  • 49
  • 67

2 Answers2

4

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()
                    }
            }
    })
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Hello! This is a little off-topic but it might improve my future answers: how do you make those pretty-looking animated images? I'd love to know. :-) – Fabian Apr 26 '20 at 19:53
  • @Asperi This works, and I like the alignmentGuide override (as long as it gets the job done, I don't have to use GeometryReader). But, when I use $0.height it moves it only by $0.height/2 -- do you have an answer why? This is a question about alignmentGuide in general, not your answer in particular. – andrewz Apr 26 '20 at 20:07
1

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)
            }
    }
}
Fabian
  • 5,040
  • 2
  • 23
  • 35
  • This is an alternative, however I don't want the content view to be centered initially - I want it to hug the top edge of the screen, and then move up so that only a portion of it is visible (so the user can tap it to move it back). – andrewz Apr 26 '20 at 20:08
  • I didn't realize this at first, so I updated the answer accordingly. – Fabian Apr 26 '20 at 22:12