Skip to main content

How to detect when the size or position of a view changes

About 3 minSwiftSwiftUIArticle(s)bloghackingwithswift.comcrashcourseswiftswiftuixcodeappstore

How to detect when the size or position of a view changes 관련

SwiftUI by Example

Back to Home

How to detect when the size or position of a view changes | SwiftUI by Example

How to detect when the size or position of a view changes

Updated for Xcode 16

New in iOS 18

SwiftUI’s onGeometryChange() modifier lets us track when a view's frame, size, or safe area insets change, then take any action as a result. Additionally, this modifier will also report the initial value of whatever you're watching, so it's helpful for both one-off and continuous monitoring.

For example, we could print the frame of a view when it's first created and whenever it changes, like this:

Text("Hello, world")
    .onGeometryChange(for: CGRect.self) { proxy in
        proxy.frame(in: .global)
    } action: { newValue in
        print("Frame is now \(newValue)")
    }

Download this as an Xcode projectopen in new window

Frame refers to the view's size and position, which means that rotating your device or changing window size is likely to trigger the action closure.

Breaking this modifier down, you'll see it accepts three values:

  1. The type of value you're interested in. We're watching a CGRect in the code above, but you might also use Double if you only wanted one value, or perhaps use Bool if the thing you're watching can be boiled down to a simple true or false.
  2. A transformation closure, which is given a GeometryProxy and should return a value of whatever you specified, e.g. a CGRect. This is the value SwiftUI will watch for changes going forward.
  3. An action closure, which will be triggered whenever the watched value changes.

Important

Although you can use the action closure to set view state, make sure you don't accidentally get stuck in a layout loop. For example, the code below gets into a loop because it tries to display the view's size in the view itself:

struct ContentView: View {
    @State private var textFrame = CGRect.zero

    var body: some View {
        Text("Size is: \(textFrame)")
            .onGeometryChange(for: CGRect.self) { proxy in
                proxy.frame(in: .global)
            } action: { newValue in
                textFrame = newValue
            }
    }
}

Being able to track a view's size and position unlocks a variety of abilities, such as being able to create views elsewhere with a matched size. For example, in the code below the rectangle always has the same frame as the text view:

struct ContentView: View {
    @State private var textFrame = CGRect.zero
    @State private var textSize = 17.0

    var body: some View {
        VStack {
            Text("Hello, world")
                .font(.system(size: textSize))
                .onGeometryChange(for: CGRect.self) { proxy in
                    proxy.frame(in: .global)
                } action: { newValue in
                    textFrame = newValue
                }

            Rectangle()
                .frame(width: textFrame.width, height: textFrame.height)

            Slider(value: $textSize, in: 10...30)
                .padding()
        }
    }
}

Download this as an Xcode projectopen in new window

Similar solutions…
How to read the size and position of a scrollview | SwiftUI by Example

How to read the size and position of a scrollview
How to position and style subviews that come from a different view | SwiftUI by Example

How to position and style subviews that come from a different view
How to position views in a grid using LazyVGrid and LazyHGrid | SwiftUI by Example

How to position views in a grid using LazyVGrid and LazyHGrid
How to adjust the position of a view using its offset | SwiftUI by Example

How to adjust the position of a view using its offset
How to position views in a fixed grid | SwiftUI by Example

How to position views in a fixed grid

이찬희 (MarkiiimarK)
Never Stop Learning.