What are the coordinate spaces in SwiftUI, and how do you read a view’s frame?

8.8.2 · L3 · Interview

Predict first

A card needs its position within a scroll view, which may itself be inside a sheet. Which space should it use?

// A: .global
// B: a named coordinate space set on the scroll view
// C: .local
  • A
  • B
  • C

Mental model. Coordinates need an address book: “3 metres from the door” (named space) is not the same as “3 metres from the building’s front gate” (global).

Every position or frame is meaningful only relative to a coordinate space. SwiftUI has three kinds. .local is the view’s own space, with the origin at its top-left. .global is the screen’s (or window’s) space. .named("name") is a space you create with .coordinateSpace(name: "name") on an ancestor, so a child can express its frame relative to it. Newer SDKs add .coordinateSpace(.named(…)) typed forms and the scroll view’s own space. You read a frame with a GeometryProxy: proxy.frame(in: .global), or proxy.frame(in: .named("scroll")), and gestures give locations in a chosen space too, via DragGesture(coordinateSpace:). proxy.size is always the view’s own size.

Use a named space on the container when you need “where is this inside that”: a sticky header, a parallax effect, drag-and-drop targets, a tooltip anchored to an item. Using .global is fragile in split screen, sheets and windows, where the global origin is not what you expect. Combine with preferences to carry the measured frame up to the view that needs it, and with anchorPreference and Anchor<CGRect> when you want a frame resolved in the reader’s space without choosing a space yourself. Remember that frames read during layout may be a pass behind, and that animated offsets change what you read: measure the thing you care about, not its animated wrapper.

A named space on the scroll view is independent of where the scroll view sits on the screen, so it stays correct in sheets and split views.

Common traps

  • Assuming .global is the screen. In a sheet, split view or Stage Manager window it is not what you think; name a container space.
  • Reading a frame from an animated wrapper. You get the animated frame; measure the view you actually care about.

They might follow up with

  • How do you make a drag gesture report positions relative to a container? Use a named space on the container and DragGesture(coordinateSpace: .named("container")).
  • How do you carry a frame to an ancestor? A preference key or an anchor preference.

Related questions

  • 8.8.1 What is a PreferenceKey, and how do values travel from children up to ancestors?
  • 8.5.2 When do you use GeometryReader, and how do safe areas work in SwiftUI?
  • 7.2.2 What is the difference between frame and bounds, and what happens to them under a transform?

← What is a PreferenceKey, and how do values travel from children up to…