I think there are three reasons why it is not working as you expected:
You seem to be expecting that GeometryProxy.frame
gives you the frame of the container with the coordinate space. It is actually giving you the frame of the GeometryReader
, which has the black border.
I would assume that coordinate spaces do not work across stacks. So the item being positioned has to be inside the coordinate space you're trying to reference.
The GeometryReader
also needs to be inside the coordinate space you're trying to reference.
Of course, the item being positioned also needs to be inside the GeometryReader
, otherwise the GeometryProxy
will not be in scope.
What this all means, is that using .position
in connection with a GeometryProxy.frame
is only going to be useful when the GeometryReader
is not occupying the full container space.
Looking at your example:
- I would expect that minX and minY are delivered using the global coordinate space, because the yellow coordinate space is unknown to both the pink square and the
GeometryProxy
. This means, minX will be 0 and minY will be the height of the safe area inset at the top of the display.
- The pink square is half out of view horizontally, because
.position
positions a view by its center.
- It is just a coincidence that the pink square appears to align with the cyan rectangle on its top edge. If you run it on a device with different top insets (such as an iPhone 14 Pro) then it is clearly not aligned.
- The
ZStack
is extending to the full bounds of the GeometryReader
as a consequence of having an item in the ZStack
being positioned using .position
(this being the pink square). See How does .position modifier change layout of parent Stack in SwiftUI? for more elaboration.
- The reason why the background is gray all the way to the screen edges is because when a background is set using
.background()
(in other words, with round parenthesis taking a ShapeStyle
as parameter, as opposed to curly braces which would be a ViewBuilder
function), it ignores safe areas by default.
So here is an adapted example:
- The container for the yellow coordinate space is now a
VStack
- The
GeometryReader
containing the pink square has been moved inside this VStack
.
- A (new) orange Rectangle comes before the
GeometryReader
in the same VStack
.
- The horizontal position of the pink square uses minX from the cyan coordinate space.
- The vertical position of the pink square uses minY from the yellow coordinate space.
- An adjustment of half the square's width and half the square's height is added to the x and y positions. This adjustment is in order to position the square by its top-left corner, instead of by its center.
var body: some View {
ZStack {
Color.clear // expand ZStack to max bounds
Group {
VStack(spacing: 0) {
Rectangle()
.foregroundColor(.orange)
.frame(height: 25)
GeometryReader { proxy in
Rectangle()
.fill(.pink)
.frame(width: 100, height: 100)
// .position positions the centre of view,
// adjustment of half width and half height
// needed to position top-left corner
.position(
// The frame being referenced here is the frame
// of the GeometryReader (with black border),
// not the frame of the container.
x: proxy.frame(in: .named("CyanRectangle")).minX + 50,
y: proxy.frame(in: .named("YellowRectangle")).minY + 50
)
.coordinateSpace(name: "PinkRectangle")
}
.border(.black, width: 1)
}
.frame(width: uiScreenWidth * 0.6, height: uiScreenHeight * 0.6)
.coordinateSpace(name: "YellowRectangle")
.background { Color.yellow }
}
.frame(width: uiScreenWidth * 0.9, height: uiScreenHeight * 0.9)
.coordinateSpace(name: "CyanRectangle") // must follow after frame!
.background { Color.cyan }
}
.background(.gray)
}

Observations:
- The pink square is shifted horizontally across by the same amount as the yellow rectangle is nested inside the cyan space, this being the offset of the
GeometryReader
inside the cyan coordinate space.
- The pink square is shifted vertically down by the height of the orange rectangle, this being the offset of the
GeometryReader
inside the yellow coordinate space.
- Something important that I discovered: the
coordinateSpace
needs to be defined after the frame is set on the container!
In conclusion, using coordinate spaces like this is quite a handy way of finding the dimensions of content outside the GeometryReader
, when this other content is impacting the position of the GeometryReader
inside a common container.