SwiftUI Field Guide

GeometryReader

A GeometryReader is primarily used to measure geometry. It gives us access to the proposed size through a geometry proxy, and the proxy also exposes related values such as the safe area insets and frame information.

The caveat is that a geometry reader always accepts the proposed size. Because of that, it is easy to change layout accidentally while trying to measure something. In practice, we often use geometry readers in overlays or backgrounds so they can measure a view without influencing its size.

Reading the Proposed Size

The core behavior is simple: the geometry reader accepts the proposed size, and reports that size to its closure through the proxy. In other words, proxy.size tells us what size the geometry reader itself ended up with. By default, the geometry reader places its contents at the top-leading corner.

Code
GeometryReader { proxy in    Text(        "\(proxy.size.width) x \(proxy.size.height)"    )        .font(.caption)        .foregroundStyle(.white)        .padding(6)        .background(.blue)        .fixedSize()}.border(.blue).padding()
Preview
260

Measuring a View

When we want to measure an existing view, the most useful pattern is to put the geometry reader in an overlay or background. The secondary view of an overlay gets proposed the size of the primary view, so the geometry reader can read that size without changing the layout. This is also the pattern we use on the debugging page.

Code
Text("Measure me without changing layout.")    .padding()    .background {        Rectangle()            .fill(.orange)            .opacity(0.2)    }    .overlay {        GeometryReader { proxy in            Text(                "\(proxy.size.width) x \(proxy.size.height)"            )                .font(.caption)                .foregroundStyle(.white)                .padding(6)                .background(.blue)                .fixedSize()                .padding()        }    }
Preview
240

We can also see this behavior in the layout tree. The geometry reader unconditionally accepts the proposal, and then places its child at the top-leading corner within that region.

Code
GeometryReader { proxy in    Text("Hello")        .padding()}
Preview
Tree

 

1/1
220

When It Causes Trouble

Many problems with geometry readers come from putting them directly around a view we wanted to measure. Because the geometry reader always becomes the proposed size, it can suddenly absorb flexible space inside containers such as HStack and VStack.

Code
HStack(spacing: 12) {    Text("Fixed")        .padding(10)        .frame(width: 70, height: 80)    GeometryReader { proxy in        Text(            "reader: \(proxy.size.width) x \(proxy.size.height)"        )            .font(.caption)            .foregroundStyle(.white)            .padding(6)            .background(.blue)            .fixedSize()    }    .border(.mint)    Text("Fixed")        .padding(10)        .frame(width: 70, height: 80)}.padding(12)
Preview
320

There are two common ways to avoid this. We can either put the geometry reader in an overlay or background, or we can wrap something that is already completely flexible, such as a scroll view or another view that would have accepted the proposed size anyway.

Geometry Proxy

The proxy also provides access to safe area information. We cover that in more detail in the Geometry Reader section of the safe area page, because that interaction has its own set of rules.

A geometry proxy can also read frames in different coordinate spaces and resolve anchors. These features become useful in more advanced layout techniques, especially when we need to communicate geometry up the view tree or convert measurements between local, global, and named spaces. We'll come back to those patterns elsewhere rather than adding them here.

The proxy describes the geometry reader itself, not some abstract descendant. If we want to know how large a specific view became after layout, the measurement usually has to happen exactly where that view is rendered.