💡 How to Obtain View Dimensions in SwiftUI

Get weekly handpicked updates on Swift and SwiftUI!

TL;DR: Use GeometryReader, onGeometryChange , visualEffect, or containerRelativeFrame to dynamically retrieve and respond to view dimensions in SwiftUI. Each method caters to specific use cases and levels of customization.

Background

Dynamically retrieving the dimensions of a view is a common requirement in SwiftUI, especially in scenarios where layouts need to respond to size changes. This article introduces several methods to obtain view dimensions and their applicable use cases.

Using GeometryReader

GeometryReader is a highly adaptable method compatible with all SwiftUI versions. It is often used with overlay or background to avoid interfering with the main view’s layout.

Example Code

Swift
blueRectangle
  .background(
    GeometryReader { proxy in
      Color.clear // Create a transparent view matching the main view's dimensions
        .task(id: proxy.size) {
          size = proxy.size // Retrieve and monitor size changes
        }
    }
  )

Features

  • Encapsulates size retrieval logic within background, ensuring no impact on the main view’s layout.
  • Uses task(id:) to fetch dimensions immediately when the view loads and update them when proxy.size changes.

Using onGeometryChange

Starting from iOS 16, onGeometryChange provides a more concise way to monitor size changes.

Example Code

Swift
struct SizeDemo: View {
    @State var size: CGSize?
    var body: some View {
        Rectangle()
            .onGeometryChange(for: CGSize.self) { proxy in
                proxy.size
            } action: {
                size = $0
            }
    }
}

iOS 18+ New Feature

Supports retrieving both old and new size values, making it easier to handle size change logic.

Swift
.onGeometryChange(for: CGSize.self) { proxy in
    proxy.size
} action: { old, new in
    size = new // Retrieve the new size
    print("Old size: \(old), New size: \(new)")
}

Using visualEffect

For dynamically applying visual effects based on view dimensions (e.g., offset, scaleEffect), visualEffect (iOS 17+) provides a more direct approach.

Example Code

Swift
struct SizeDemo: View {
    var body: some View {
        Rectangle()
            .foregroundStyle(.red)
            .visualEffect { effect, proxy in
                effect
                    .offset(y: proxy.size.height / 3) // Offset the view by 1/3 of its height
            }
    }
}

Effect

visualeffect-swiftui-demo

Using containerRelativeFrame

containerRelativeFrame (iOS 17+) combines the functionality of GeometryReader and frame, allowing you to retrieve the size of the view’s parent container (e.g., window, NavigationStack, or ScrollView) and apply it as a constraint.

Example Code

The following code sets a rectangle’s width to half and height to a quarter of the window’s size:

Swift
struct TransformsDemo: View {
    var body: some View {
        Rectangle()
            .containerRelativeFrame([.horizontal, .vertical]) { length, axis in
                if axis == .vertical {
                    return length / 4
                } else {
                    return length / 2
                }
            }
    }
}
  • containerRelativeFrame searches upward for the nearest container, which in this case is the window.

Effect

containerRelativeFrame

Container Adaptability

containerRelativeFrame automatically selects the appropriate container. For instance, when placed within a NavigationStack, the dimensions are calculated relative to the NavigationStack.

Swift
struct TransformsDemo: View {
    var body: some View {
        NavigationStack {
            Rectangle()
                .containerRelativeFrame([.horizontal, .vertical]) { length, axis in
                    if axis == .vertical {
                        return length / 4
                    } else {
                        return length / 2
                    }
                }
        }
        .frame(width: 300, height: 300) // Set the size of the NavigationStack
        .border(.red, width: 4)
    }
}

Effect

containerRelativeFrame-in-navigationStack

Application in ScrollView

containerRelativeFrame can also be used to dynamically adjust the size of child views within a scroll view. For example, setting the width of child views to one-third of the scroll view’s width:

Swift
struct ScrollViewDemo: View {
    var body: some View {
        ScrollView(.horizontal) {
            HStack(spacing: 10) {
                ForEach(0..<10) { _ in
                    Rectangle()
                        .fill(.purple)
                        .aspectRatio(3 / 2, contentMode: .fit)
                        .containerRelativeFrame(.horizontal, count: 3, span: 1, spacing: 0)
                }
            }
        }
    }
}

Effect

containerRelativeFrame-scrollView

Method Comparison

MethodUse CaseFeatures
GeometryReaderApplicable to all SwiftUI versionsHighly flexible but requires more custom code
onGeometryChangeSimplifies size change monitoring (iOS 16+)Clear semantics, supports old/new size (iOS 18+)
visualEffectFor dynamic rendering effects (iOS 17+)Simple and efficient, directly uses GeometryProxy
containerRelativeFrameRelative size calculation for containers (iOS 17+)Automatically adapts to container context

Further Reading