SwiftUI
View Layout
Swift Programming
iOS Development
User Interface

can I get the position of a View after layout in SwiftUI?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

SwiftUI does not expose a direct “give me this view’s final frame” API in the same imperative style as UIKit. Instead, layout information is read through GeometryReader, coordinate spaces, and preference keys after SwiftUI has performed layout. The practical answer is yes, you can get a view’s position, but you must do it in a SwiftUI-native way.

Use GeometryReader to Read Frame Information

GeometryReader lets you inspect a view’s size and frame in a chosen coordinate space. The most common approach is attaching it in the background of the target view so it does not disturb layout.

swift
1import SwiftUI
2
3struct PositionDemo: View {
4    var body: some View {
5        Text("Hello")
6            .padding()
7            .background(
8                GeometryReader { proxy in
9                    Color.clear
10                        .onAppear {
11                            print(proxy.frame(in: .global))
12                        }
13                }
14            )
15    }
16}

Using Color.clear avoids rendering visible content while still giving access to the geometry proxy.

That background pattern is important because it lets you measure the view that SwiftUI already laid out, rather than asking a geometry container to propose a new size and change the result you were trying to inspect.

Understand Coordinate Spaces

The same view can have different reported positions depending on which coordinate space you ask for.

Choices include:

  • '.local for the view’s own local coordinate space.'
  • '.global for the screen-level coordinate system.'
  • '.named(...) for a custom coordinate space you define.'

Named spaces are often the most useful for parent-child layout relationships.

swift
1import SwiftUI
2
3struct NamedSpaceDemo: View {
4    var body: some View {
5        VStack {
6            Text("Tracked")
7                .background(
8                    GeometryReader { proxy in
9                        Color.clear
10                            .onAppear {
11                                print(proxy.frame(in: .named("container")))
12                            }
13                    }
14                )
15        }
16        .coordinateSpace(name: "container")
17    }
18}

This is more stable than relying on .global when the view is inside scroll views or nested containers.

Use Preference Keys for Reusable Position Reporting

If the parent view needs a child’s position reactively, preference keys are the clean SwiftUI pattern.

swift
1import SwiftUI
2
3struct ViewFrameKey: PreferenceKey {
4    static var defaultValue: CGRect = .zero
5
6    static func reduce(value: inout CGRect, nextValue: () -> CGRect) {
7        value = nextValue()
8    }
9}
10
11struct ChildView: View {
12    var body: some View {
13        Text("Track me")
14            .background(
15                GeometryReader { proxy in
16                    Color.clear.preference(
17                        key: ViewFrameKey.self,
18                        value: proxy.frame(in: .global)
19                    )
20                }
21            )
22    }
23}
24
25struct ParentView: View {
26    @State private var frame: CGRect = .zero
27
28    var body: some View {
29        ChildView()
30            .onPreferenceChange(ViewFrameKey.self) { newValue in
31                frame = newValue
32                print(frame)
33            }
34    }
35}

This is the pattern to use when layout changes over time and the parent needs updates.

Avoid Disturbing Layout While Measuring

A common mistake is wrapping the target view itself in GeometryReader, which often changes layout because the reader expands to available space. Measuring in .background or .overlay is usually safer.

That distinction matters a lot in stacks, lists, and scroll containers where the parent’s size proposal strongly affects child layout.

Reading Position in Scroll Views

Inside a ScrollView, global coordinates change as the user scrolls. If you need scroll-relative position, use a named coordinate space on the scroll container and measure in that space rather than .global.

This keeps the values meaningful for visibility logic, sticky headers, or scroll-triggered effects.

It also makes the code easier to reason about later because the reported frame stays tied to the container you actually care about, not to the whole screen.

When UIKit Bridging Is Still Useful

If you need highly imperative frame observation or integration with existing UIKit code, a UIViewRepresentable bridge can sometimes be justified. But for most SwiftUI layout tasks, geometry plus preferences is the more idiomatic and maintainable route.

The goal should be to keep layout observation declarative unless you have a strong integration reason not to.

Common Pitfalls

  • Using GeometryReader as a wrapper and accidentally changing layout.
  • Reading .global coordinates when a local or named space would be more stable.
  • Expecting the frame value to be valid before SwiftUI has completed layout.
  • Storing geometry in a way that creates unnecessary update loops.
  • Using UIKit-style layout assumptions in purely SwiftUI view hierarchies.

Summary

  • You can read a SwiftUI view’s position after layout, but the idiomatic tools are GeometryReader and preference keys.
  • Measuring in background or overlay is usually safer than wrapping the whole view.
  • Choose the right coordinate space for the question you are trying to answer.
  • Use preference keys when parent views need child layout information reactively.
  • Keep the solution declarative unless UIKit interop is truly necessary.

Course illustration
Course illustration

All Rights Reserved.