Search code examples
iosswiftlayoutswiftuiframe

Where is the 'Origin' of a View in SwiftUI


I was playing around with .position(x:y:) and CoordinateSpace.global and I came across something that confused me.

I was trying to determine the coordinates of the origin of a ZStack in the global coordinate space. However, when I placed a dot on those coordinates, it didn't line up with the top left corner of the ZStack.

This was the code I used:

struct Test: View {    
    var body: some View {
        ZStack {
            Image(systemName: "circle.fill")
                .resizable()
                .frame(width: 5, height: 5)
                .foregroundColor(.red)
                .position(x: 177, y: 423)  // This is from 'frame.origin.x' and 'frame.origin.y'
            
            ZStack {  // Trying to determine this ZStack's coordinates in the global coordinate space
                GeometryReader { geometry -> AnyView in  // Used to retrieve the coordinates using `geometry` and then returning a Text so I can see the coordinates on screen
                    let frame = geometry.frame(in: CoordinateSpace.global)
                    return AnyView(Text("\(frame.origin.x), \(frame.origin.y)").fixedSize(horizontal: false, vertical: true))
                }
            }
            .frame(width: 60, height: 60)
        }
    }
}

And this is where the dot showed up:
Notice how the dot isn't at the top left corner

Does anyone know why it showed up at that weird place when it should have appeared on the top left of the ZStack? I thought the origin was supposed to be at the top left of a view?


Solution

  • You calculate frame by geometry reader in global coordinate space but Image is placed in outer ZStack coordinate space, that is why misplacement.

    To make it work as expected (accepting for test purpose hardcoded coordinates) the coordinates should be made in one coordinate space.

    Here is a solution

    demo

    var body: some View {
        ZStack {
            Image(systemName: "circle.fill")
                .resizable()
                .frame(width: 5, height: 5)
                .foregroundColor(.red)
                .position(x: 177, y: 379)  // This is from 'frame.origin.x' and 'frame.origin.y'
    
            ZStack {
                GeometryReader { geometry -> AnyView in
                    let frame = geometry.frame(in: .named("test"))               // << here !!
                    return AnyView(Text("\(frame.origin.x), \(frame.origin.y)")
                        .fixedSize(horizontal: false, vertical: true))
                }
            }
            .frame(width: 60, height: 60)
        }.coordinateSpace(name: "test")       // << here !!
    }