What is Geometry Reader in SwiftUI?

I am learning SwiftUI. And I come across to "GeometryReader". And I want to know why and when to use it?


Solution 1:

UPDATE

Since I posted the answer, I have also written an article on how GeometryReader works. Check it out for a more detailed explanation: https://swiftui-lab.com/geometryreader-to-the-rescue/


GeometryReader is a view that gives you access to the size and position of it's parent. For example:

struct MyView: View {
    var body: some View {
        GeometryReader { geometry in
           // Here goes your view content,
           // and you can use the geometry variable
           // which contains geometry.size of the parent
           // You also have function to get the bounds
           // of the parent: geometry.frame(in: .global)
        }
    }
}

I usually combine it with .background() to obtain some other view's bounds. For example, The Text view is hard to predict how large it would be in advance. When I need that information, I use this trick:

First I have defined a view called GeometryGetter:

struct GeometryGetter: View {
    @Binding var rect: CGRect
    
    var body: some View {
        return GeometryReader { geometry in
            self.makeView(geometry: geometry)
        }
    }
    
    func makeView(geometry: GeometryProxy) -> some View {
        DispatchQueue.main.async {
            self.rect = geometry.frame(in: .global)
        }

        return Rectangle().fill(Color.clear)
    }
}

Then, to get the bounds of a Text view (or any other view):

struct MyView: View {
    @State private var rect: CGRect = CGRect()

    var body: some View {
        Text("some text").background(GeometryGetter($rect))

        // You can then use rect in other places of your view:
        Rectangle().frame(width: 100, height: rect.height)
    }
}

For some use cases, I posted some answers to other questions that use GeometryReader. Check them out:

Move textfields to avoid being hidden by the keyboard: https://stackoverflow.com/a/56721268/7786555

How to make view the size of another view in SwiftUI: https://stackoverflow.com/a/56661706/7786555

Note

In GeometryGetter, I added a DispatchQueue.main.async {} to set the rect. In some cases it could lead to runtime warning otherwise: Modifying state during view update.

Solution 2:

What is called Reader in SwiftUI?

In addition to the kontiki's answer, Readers are container views that define their content as a function. So they can have some access and abilities about their parent. They are generic structs if you look more closely and there is 2 readers available now in SwiftUI 2.0:

Note that it's just a convention, they're not conforming special protocol more of the View protocl.

GeometryReader

struct GeometryReader<Content: View> : View

This is A container view that defines its content as a function of its own size and coordinate space. So you can detect frame and position changes and the current state of any view within a GeometryReader. One of the popular usage of this reader that when you need separate views in separate stacks have the same (or relative) sizes.


ScrollViewReader

struct ScrollViewReader<Content: View> : View

This is view whose child is defined as a function of a ScrollViewProxy targeting the scrollable views within the child. So you can have some access to the scrollview like scrolling to a specific item in a list or similar stuff.

To minimalizing the duplication, I didn't post examples, you can check the link for more information if you want