Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can we make a Custom GeometryReader in SwiftUI?

I was wondering how GeometryReader works under cover, and I am interested to build a custom GeometryReader for learning purpose!

Frankly I think every single view that we use in body is kind of GeometryReaderView with this difference that they do not use a closure for sending the proxy for us and it would annoying that every single view call back it's proxy! Therefore apple decided to give Geometry reading function to GeometryReader! So it is just my thoughts!

So I am looking a possible and also more likely SwiftUI-isch approach to reading proxy of view, or in other words see my codes in down:

struct ContentView: View {
    
    var body: some View {
        
        CustomGeometryReaderView { proxy in
            
            Color.red
                .onAppear() {
                    print(proxy)
                }
            
        }
        
    }
    
}

struct CustomGeometryReaderView<Content: View>: View {
    
    @ViewBuilder let content: (CGSize) -> Content
    
    var body: some View {
        
        // Here I most find a way to reading the available size for CustomGeometryReaderView and reporting it back!
        return  Color.clear.overlay(content(CGSize(width: 100.0, height: 100.0)), alignment: .topLeading)
        
    }
    
}

Also I know that reading and reporting proxy of a view is not just the size of view, also it is about CoordinateSpace, frame ... But for now for making things easier to solve I am just working on size! So size matter!

As I said I am not interested to working with UIKit or UIViewRepresentable for reading the size! May apple using something like that under cover or may not! My goal was trying solve the issue with pure SwiftUI or may some of you have some good link about source code of GeometryReader for reading and learning of it.

like image 578
ios coder Avatar asked Oct 29 '25 17:10

ios coder


1 Answers

Ok, there are several instruments in SwiftUI providing access to view size (except GeometryReader of course).

The problem of course is to transfer that size value into view build phase, because only GeometryReader allows to do it in same build cycle.

Here is a demo of possible approach using Shape - a shape by design has no own size and consumes everything available, so covers all area, and has been provided that area rect as input.

Tested with Xcode 13 / iOS 15

struct CustomGeometryReaderView<Content: View>: View {

    @ViewBuilder let content: (CGSize) -> Content

    private struct AreaReader: Shape {
        @Binding var size: CGSize

        func path(in rect: CGRect) -> Path {
            DispatchQueue.main.async {
                size = rect.size
            }
            return Rectangle().path(in: rect)
        }
    }

    @State private var size = CGSize.zero

    var body: some View {
        // by default shape is black so we need to clear it explicitly
        AreaReader(size: $size).foregroundColor(.clear)
            .overlay(Group {
                if size != .zero {
                    content(size)
                }
            })
    }
}

Alternate: same, but using callback-based pattern

struct CustomGeometryReaderView<Content: View>: View {

    @ViewBuilder let content: (CGSize) -> Content

    private struct AreaReader: Shape {
        var callback: (CGSize) -> Void

        func path(in rect: CGRect) -> Path {
            callback(rect.size)
            return Rectangle().path(in: rect)
        }
    }

    @State private var size = CGSize.zero

    var body: some View {
        AreaReader { size in
            if size != self.size {
                DispatchQueue.main.async {
                    self.size = size
                }
            }
        }
        .foregroundColor(.clear)
        .overlay(Group {
            if size != .zero {
                content(size)
            }
        })
    }
}
like image 68
Asperi Avatar answered Oct 31 '25 08:10

Asperi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!