SwiftUI’s GeometryReader
allows us to use its size and coordinates to determine a child view’s layout, and it’s the key to creating some of the most remarkable effects in SwiftUI.
You should always keep in mind SwiftUI’s three-step layout system when working with GeometryReader
: parent proposes a size for the child, the child uses that to determine its own size, and parent uses that to position the child appropriately.
In its most basic usage, what GeometryReader
does is let us read the size that was proposed by the parent, then use that to manipulate our view. For example, we could use GeometryReader
to make a text view have 90% of all available width regardless of its content:
struct ContentView: View {
var body: some View {
GeometryReader { proxy in
Text("Hello, World!")
.frame(width: proxy.size.width * 0.9)
.background(.red)
}
}
}
That proxy
parameter that comes in is a GeometryProxy
, and it contains the proposed size, any safe area insets that have been applied, plus a method for reading frame values that we’ll look at in a moment.
GeometryReader
has an interesting side effect that might catch you out at first: the view that gets returned has a flexible preferred size, which means it will expand to take up more space as needed. You can see this in action if you place the GeometryReader
into a VStack
then put some more text below it, like this:
struct ContentView: View {
var body: some View {
VStack {
GeometryReader { proxy in
Text("Hello, World!")
.frame(width: proxy.size.width * 0.9, height: 40)
.background(.red)
}
Text("More text")
.background(.blue)
}
}
}
You’ll see “More text” gets pushed right to the bottom of the screen, because the GeometryReader
takes up all remaining space. To see it in action, add background(.green)
as a modifier to the GeometryReader
and you’ll see just how big it is. Note: This is a preferred size, not an absolute size, which means it’s still flexible depending on its parent.
When it comes to reading the frame of a view, GeometryProxy
provides a frame(in:)
method rather than simple properties. This is because the concept of a “frame” includes X and Y coordinates, which don’t make any sense in isolation – do you want the view’s absolute X and Y coordinates, or their X and Y coordinates compared to their parent?
SwiftUI calls these options coordinate spaces, and those two in particular are called the global space (measuring our view’s frame relative to the whole screen), and the local space (measuring our view’s frame relative to its parent). We can also create custom coordinate spaces by attaching the coordinateSpace()
modifier to a view – any children of that can then read its frame relative to that coordinate space.
To demonstrate how coordinate spaces work, we could create some example views in various stacks, attach a custom coordinate space to the outermost view, then add an onTapGesture
to one of the views inside it so it can print out the frame globally, locally, and using the custom coordinate space.
Try this code:
struct OuterView: View {
var body: some View {
VStack {
Text("Top")
InnerView()
.background(.green)
Text("Bottom")
}
}
}
struct InnerView: View {
var body: some View {
HStack {
Text("Left")
GeometryReader { proxy in
Text("Center")
.background(.blue)
.onTapGesture {
print("Global center: \(proxy.frame(in: .global).midX) x \(proxy.frame(in: .global).midY)")
print("Custom center: \(proxy.frame(in: .named("Custom")).midX) x \(proxy.frame(in: .named("Custom")).midY)")
print("Local center: \(proxy.frame(in: .local).midX) x \(proxy.frame(in: .local).midY)")
}
}
.background(.orange)
Text("Right")
}
}
}
struct ContentView: View {
var body: some View {
OuterView()
.background(.red)
.coordinateSpace(name: "Custom")
}
}
The output you get when that code runs depends on the device you’re using, but here’s what I got:
Those sizes are mostly different, so hopefully you can see the full range of how these frame work:
OuterView
because we attach it in ContentView
. This number matches the global position because OuterView
runs edge to edge horizontally.OuterView
. This value is smaller than the global center Y because OuterView
doesn’t extend into the safe area.Which coordinate space you want to use depends on what question you want to answer:
SAVE 50% All our books and bundles are half price for Black Friday, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.
Link copied to your pasteboard.