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.
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)
}
})
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With