跳转至
Structure

GeometryReader

A type that reads the geometry of its container to produce a view.

Declaration

@frozen struct GeometryReader<Content> : View where Content : View

Overview

How it works

GeometryReader can be expressed simply with the following pseudocode:

struct GeometryReader<Content: View> {
    var content: (GeometryProxy) -> Content

    var body: some View {
        content(<container geometry>)
    }
}
In the implementation above, is an instance of GeometryProxy. GeometryProxy simply encapsulates the container's frame and safe area insets, provided at runtime by SwiftUI.

GeometryReader to get container bounds

In this example, GeometryReader is used to create a view scaled down to exactly half of its parent container:

struct ExampleView: View {
    var body: some View {
        GeometryReader { (proxy: GeometryProxy) in
            Color.green
                .frame(
                    width: proxy.size.width / 2,
                    height: proxy.size.height / 2
                )
        }
    }
}
A view displaying a green geometry reader frame in the upper left quadrant of the view.

Note: GeometryReader fills into its parent container, and the current default alignment of its content is topLeading. The example above results in a green rectangle aligned to the top left corner of the screen, inset by the screen's safe area. The alignment cannot be overriden, and is liable to change in the future.

GeometryReader to get a view's frame

GeometryReader can also be used with background(_:alignment:), to acquire the geometry of a target view. Consider redView in the following example:

struct ExampleView: View {
    var redView: some View {
        Rectangle()
            .fill(Color.red)
            .frame(width: 500, height: 500)
    }

    @State var someFrame: CGRect? // will be updated after the first layout pass

    var body: some View {
        VStack {
            redView
                .frame(width: 500, height: 500)
                .background(
                    GeometryReader { (proxy: GeometryProxy) -> EmptyView in
                        if someFrame != proxy.frame(in: .global) {
                            DispatchQueue.main.async {
                                someFrame = proxy.frame(in: .global)
                            }
                        }

                        return EmptyView()
                    }
                )
            Text("Hello, World!")
        }
    }
}
A view containing a VStack composed of a red background geometry reader frame with the text "Hello, World!" below it.

Note that someFrame will be updated to hold the frame of SomeView, not the frame of its container, the VStack. This is achieved by forcing a GeometryReader as a background of SomeView, thereby constraining it to SomeView's bounds.

This task of acquiring a view's frame can be done in a generic and reusable way using ViewModifier:

struct GetGlobalFrame: ViewModifier {
    @Binding var globalFrame: CGRect?

    func body(content: Content) -> some View {
        content.background(
            GeometryReader { (proxy: GeometryProxy) -> EmptyView in
                if globalFrame != proxy.frame(in: .global) {
                    DispatchQueue.main.async {
                        globalFrame = proxy.frame(in: .global)
                    }
                }

                return EmptyView()
            }
        )
    }
}
The modifier above can be used in the following manner:

struct ExampleView: View {
    struct SomeView: View {
        var body: some View {
            Rectangle()
                .fill(Color.red)
                .frame(width: 500, height: 500)
        }
    }

    @State var someFrame: CGRect? // will be updated after the first layout pass

    var body: some View {
        VStack {
            SomeView()
                .frame(width: 500, height: 500)
                .modifier(GetGlobalFrame(globalFrame: $someFrame))

            Text("Hello, World!")
        }
    }
}

struct GetGlobalFrame: ViewModifier {
    @Binding var globalFrame: CGRect?

    func body(content: Content) -> some View {
        content.background(
            GeometryReader { (proxy: GeometryProxy) -> EmptyView in
                if globalFrame != proxy.frame(in: .global) {
                    DispatchQueue.main.async {
                        globalFrame = proxy.frame(in: .global)
                    }
                }

                return EmptyView()
            }
        )
    }
}
A view containing a VStack composed of a red background geometry reader frame with the text "Hello, World!" below it.

Availability

iOS 13.0+

macOS 10.15+

tvOS 13.0+

watchOS 6.0+

Topics


Type Alias

Body The type of view representing the body of this view.


Instance Property

content The content displayed within the GeometryReader view.


Initializer

init(content:) Creates a GeometryReader parent view using the parent's geometry.