How to position a List using ZStack & Geometry?

Viewed 2408

I'm trying to position a ListView directly below a text field, using a ZStack and the field's geometry - its position and size. This is with a view toward creating an autocomplete picklist

Setting the offset only appears to half work.

So far it looks as follows.

Emulator and device appears as on the right:

enter image description here

Some useful information here:

https://swiftui-lab.com/geometryreader-to-the-rescue/

import SwiftUI

struct TestView: View {

    @State private var firstname = ""
    @State private var lastname = ""
    @State private var townCity = ""

    @State private var rect: CGRect = CGRect()

    var body: some View {
        ZStack (alignment: .topLeading){
            VStack{
                Form {
                    Section {
                        TextField("Firstname", text: $firstname)
                        TextField("Lastname", text: $lastname)

                        ZStack{
                            VStack (alignment: .leading, spacing: 0){
                                TextField("Town/City", text: self.$townCity)
                                    .background(GeometryGetterV2(rect: $rect))
                            }
                            .border(Color.black, width: 1)
                        }
                        Button("Save") {
                        }
                    }
                }
            }
            SelectionsPickerV2()
                .offset(x: rect.origin.x, y: rect.origin.y)
                //.offset(x: rect.minX, y: rect.minY)
                .frame(
                    width: rect.size.width,
                    height: rect.size.height * 7)
        }
        .coordinateSpace(name: "myZstack")
    }
}

struct SelectionsPickerV2: View {

    var body: some View {
        VStack (alignment: .leading) {
            List{
                Text("Sydney, Australia")
                Text("New York, New York")
                Text("London, UK")
                Text("Paris, France")
            }
            .background(Color.blue)
        }
        .border(Color.red, width: 1)
    }
}

struct TestView_Previews: PreviewProvider {
    static var previews: some View {
        TestView()
    }
}

struct GeometryGetterV2: View {
    @Binding var rect: CGRect

    var body: some View {
        return GeometryReader { geometry in
            self.makeView(geometry: geometry)
        }
    }

    func makeView(geometry: GeometryProxy) -> some View {
        DispatchQueue.main.async {
            self.rect = geometry.frame(in: .named("myZstack"))
            //self.rect = geometry.frame(in: .local)
            //self.rect = geometry.frame(in: .global)
        }

        return Rectangle().fill(Color.clear)
    }
}
2 Answers

As many mentioned, your ZStack being the top level view gets geometry calculations wrong as the navigation bar gets added, etc. Probably just another SwiftUI bug.

To fix it just embed your ZStack inside a new parent view.

struct TestView: View {

    // ...

    var body: some View {
        VStack { // New parent view
            ZStack (alignment: .topLeading){
                // ...
            }
            SelectionsPickerV2()
                .offset(x: rect.origin.x, y: rect.origin.y)
                .frame(
                    width: rect.size.width,
                    height: rect.size.height * 7)
            }
            .coordinateSpace(name: "myZstack") // Fixed coordinate space
        }
    }
}

What Xcode version are you using, it looks nice with mine (Xcode 11.2, beta 11B41).

enter image description here

Related