Swift and SwiftUI tutorials for Swift Developers

How to Get View Size in SwiftUI

In modern app development within the Apple ecosystem, the transition from imperative to declarative interfaces has changed the rules of the game. As an iOS Developer, facing new paradigms is part of everyday life. One of the most common challenges when making the leap from UIKit or AppKit is understanding the layout system and, more specifically, how to get the view size in SwiftUI.

Unlike UIKit, where you could simply query the frame or bounds property of a UIView at almost any time, SwiftUI handles sizes dynamically through a negotiation process between parent and child views. In this extensive Swift programming tutorial, we will break down all the techniques available in Xcode to master reading dimensions in SwiftUI, ensuring your apps run perfectly on iOS, macOS, and watchOS.


1. Understanding the Layout System in SwiftUI

Before writing a single line of Swift, it is fundamental to understand why there is no magic .size property that we can access directly. The SwiftUI rendering engine works in three fundamental steps:

  1. The parent proposes a size: The container view (for example, the entire screen or a VStack) tells the child view how much space is available.
  2. The child chooses its own size: The child view calculates how much space it actually needs based on its content (text, images, etc.) and the parent’s proposal.
  3. The parent positions the child: Finally, the container places the child view in its coordinate system.

Because of this bidirectional flow, the size of a view is not truly known until the framework has completed the rendering process. Therefore, if you are wondering how to get the view size in SwiftUI, you must know that we need to use tools that “listen” to this calculation once it has been performed.


2. The Classic Method: GeometryReader

The most well-known and used tool by any iOS Developer to read spatial dimensions is the GeometryReader. This special view takes all the space its parent offers and provides us with a GeometryProxy object, which contains detailed information about the available size and frame.

How does it work?

When you wrap your content in a GeometryReader, it reads the available space and passes it to you as a parameter. Here is a Swift programming example in Xcode:

import SwiftUI

struct GeometryReaderExample: View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Text("View width: \(geometry.size.width)")
                Text("View height: \(geometry.size.height)")
                
                Rectangle()
                    .fill(Color.blue)
                    // We use the GeometryReader size to define a relative size
                    .frame(width: geometry.size.width * 0.5, 
                           height: geometry.size.height * 0.3)
            }
            // We center the content inside the GeometryReader
            .frame(maxWidth: .infinity, maxHeight: .infinity)
        }
        .padding()
    }
}

The “Danger” of GeometryReader

Although it solves the problem of how to get the view size in SwiftUI, the GeometryReader has an important side effect: it is a “greedy” view. This means it will expand its size to occupy all the available space given by its parent. If you insert it into a VStack that would otherwise shrink to fit its content, the GeometryReader will cause the entire VStack to expand.

This makes GeometryReader excellent for full-screen relative layouts, but problematic if you only want to know the size of a small text button without altering its visual behavior.


3. The Advanced and Clean Solution: PreferenceKey

For an advanced iOS Developer, using PreferenceKey is the definitive technique. This methodology allows you to read a view’s size without altering its native layout behavior. It is an essential technique in modern Swift programming.

The idea is to place an invisible GeometryReader in the background of the view we want to measure. This reader captures the size and then sends it “up” the view hierarchy using the SwiftUI preference system.

Step 1: Create the PreferenceKey

First, we need to define a structure that conforms to the PreferenceKey protocol in Swift.

import SwiftUI

struct ViewSizeKey: PreferenceKey {
    // The initial default value
    static var defaultValue: CGSize = .zero
    
    // Function that combines values if there are multiple views sending preferences
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}

Step 2: Create an Extension or Modifier

To keep our code in Xcode clean and reusable, we are going to create an extension on View that hides all this complexity.

extension View {
    /// Modifier that reads the view size and executes a closure with the result
    func readSize(onChange: @escaping (CGSize) -> Void) -> some View {
        background(
            GeometryReader { geometryProxy in
                Color.clear
                    .preference(key: ViewSizeKey.self, value: geometryProxy.size)
            }
        )
        .onPreferenceChange(ViewSizeKey.self, perform: onChange)
    }
}

Step 3: Use the Modifier

Now we can apply this modifier to any element, no matter how small, to resolve how to get the view size in SwiftUI without breaking our design:

struct PreferenceKeyExample: View {
    @State private var textRectSize: CGSize = .zero
    
    var body: some View {
        VStack(spacing: 20) {
            Text("Dynamic Text in SwiftUI")
                .font(.largeTitle)
                .padding()
                .background(Color.orange)
                .cornerRadius(10)
                // We use our custom modifier
                .readSize { newSize in
                    self.textRectSize = newSize
                }
            
            Text("The top rectangle measures:")
                .font(.headline)
            Text("\(textRectSize.width, specifier: "%.1f") x \(textRectSize.height, specifier: "%.1f") px")
                .foregroundColor(.green)
                .bold()
        }
    }
}

This technique is completely cross-platform, working flawlessly on iOS, in resizable macOS windows, and on the small screens of watchOS.


4. The Evolution of SwiftUI: Modern Modifiers (iOS 16+)

The Apple ecosystem is constantly evolving, and the SwiftUI framework is no exception. If you are an iOS Developer building apps that only support recent operating system versions (iOS 16, macOS 13, watchOS 9 onwards), Apple has greatly simplified this process.

Through the new framework, you no longer need to create your own PreferenceKey to detect spatial changes. There is a more direct method for certain cases using onGeometryChange (available in iOS 18 / macOS 15) or by observing environment variables.

For current versions, the Swift programming community still relies heavily on the PreferenceKey pattern (as shown above) because it offers the greatest balance between backward compatibility and reliability in Xcode.


5. Cross-Platform Use Cases: iOS, macOS, and watchOS

The beauty of SwiftUI is its “Learn once, apply anywhere” philosophy. However, how to get the view size in SwiftUI can have different implications depending on the hardware:

On iOS and iPadOS

On mobile devices, views usually occupy the entire screen or respond to device rotation. Using readSize allows you, for example, to dynamically adjust the font size in a Text if the user rotates the iPhone, ensuring the content is not truncated.

On macOS

Developing for Mac in Xcode adds the variable of user-resizable windows in real-time. If you use the PreferenceKey approach, your state (@State) will update on every frame of the animation as the user drags the window border. Make sure not to perform heavy network operations or database calculations inside the .onPreferenceChange closure, as it could slow down the Mac interface.

On watchOS

The Apple Watch has very limited resources. Although the technique works, you should rarely need to read arbitrary view sizes because the watchOS layout is usually highly predictable and static (generally pure vertical scrolling). Use it sparingly to optimize performance.


6. Best Practices for the iOS Developer

As you master UI-oriented Swift programming, keep these golden guidelines in mind:

  1. Avoid Pixel Micro-management: SwiftUI is designed to be declarative. If you find yourself constantly trying to read the size of one view to calculate the exact margin of another, you are probably fighting the framework. Use Spacer(), padding(), and frame(maxWidth: .infinity) whenever possible.
  2. Beware of Infinite Rendering Loops: A classic mistake in Xcode when using GeometryReader or PreferenceKey is updating a @State that in turn changes the size of the view being measured. This will cause an Infinite Loop where the view resizes, updates the state, which resizes it again, and so on. Your app will freeze and CPU usage will hit 100%.
  3. Actively Use Previews: Utilize the Xcode Canvas to test how your sizing logic behaves on different devices and orientations in real-time.

Conclusion

Knowing exactly how to get the view size in SwiftUI is a critical competency in any professional iOS Developer‘s repertoire. Although the framework prefers that we delegate the mathematical decisions of where to place every pixel, tools like GeometryReader and PreferenceKey injection give us back absolute control when we need it.

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Article

How to Get Screen Size in SwiftUI

Related Posts