swift - 我们如何在 SwiftUI 中制作自定义 GeometryReader?

我想知道 GeometryReader 是如何隐藏的,我有兴趣为学习目的构建一个自定义 GeometryReader!

坦率地说,我认为我们在 body 中使用的每个 View 都是一种 GeometryReaderView,不同之处在于它们不使用闭包来为我们发送代理,而且每个 View 都会回调它的代理会很烦人!因此apple决定给GeometryReader赋予Geometry阅读功能!所以这只是我的想法!

所以我正在寻找一种可能而且更可能是 SwiftUI 式的方法来读取 View 的代理,或者换句话说,请查看我的代码:

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)
        
    }
    
}

我还知道, View 的读取和报告代理不仅仅是 View 的大小,它还与坐标空间、框架有关……但现在为了让事情更容易解决,我只关注大小!所以大小很重要!

正如我所说,我对使用 UIKit 或 UIViewRepresentable 来读取大小不感兴趣!苹果可能会在掩护下使用类似的东西,也可能不会! 我的目标是尝试使用纯 SwiftUI 解决问题,或者你们中的一些人可能有一些关于 GeometryReader 源代码的好链接以供阅读和学习。

最佳答案

好的,SwiftUI 中有多种工具可以访问 View 大小(当然 GeometryReader 除外)。

问题当然是将该尺寸值转移到 View 构建阶段,因为只有 GeometryReader 允许在同一构建周期中执行此操作。

这是使用 Shape 的可能方法的演示 - 设计的形状没有自己的大小并消耗所有可用的东西,因此覆盖所有区域,并且已提供该区域作为输入。

使用 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)
                }
            })
    }
}

备用:相同,但使用基于回调的模式

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)
            }
        })
    }
}

https://stackoverflow.com/questions/69918942/

相关文章:

c# - 当我更改继承类中同名方法的代码时,我不需要使用属性覆盖和虚拟。这是为什么?

angular - 检测关闭浏览器 Angular 12 的事件

javascript - 是否所有 WebAPI 都被推送到任务队列中?

python - 如何自定义条形注释以不显示选定值

node.js - 在不使用 NVM for Windows 的情况下在 Windows 上安装多个

jooq - jooq record 取数据时是否使用列索引?

amazon-web-services - AWS CodePipeline 角色无权在阶段的 "a

json - 通过 Postman 使用 PUT 请求更新 Contentful 帖子

networking - float IP 在 Digital Ocean 上的使用

android - 加载 flutter 时去除图像中的白色闪烁