简体   繁体   English

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

[英]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我想知道 GeometryReader 如何在掩护下工作,并且我有兴趣构建一个自定义 GeometryReader 以用于学习目的

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坦率地说,我认为我们在 body 中使用的每一个视图都是 GeometryReaderView 的一种,不同之处在于它们不使用闭包来为我们发送代理,而且每一个视图都回调它的代理会很烦人因此苹果决定给几何阅读function 到 GeometryReader 所以这只是我的想法

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:因此,我正在寻找一种可能且更有可能的 SwiftUI-isch 方法来读取视图代理,或者换句话说,查看我的代码:

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我也知道视图的读取和报告代理不仅仅是视图的大小,它也与 CoordinateSpace、框架有关......但现在为了让事情更容易解决,我只是在研究大小所以大小很重要

As I said I am not interested to working with UIKit or UIViewRepresentable for reading the size.正如我所说,我对使用 UIKit 或 UIViewRepresentable 来读取大小不感兴趣。 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).好的,SwiftUI 中有几个仪器可以访问视图大小(当然GeometryReader除外)。

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.问题当然是将该尺寸值转移到视图构建阶段,因为只有GeometryReader允许在同一构建周期中执行此操作。

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

Tested with Xcode 13 / iOS 15用 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)
            }
        })
    }
}

backup 备份

Whenever I need to know the frame of a view, GeometryReader is just unnecessarily complicated and the 'Bound preference tried to update multiple times per frame' warnings are horrible.每当我需要知道视图的框架时,GeometryReader 就会变得不必要地复杂,并且“绑定首选项试图每帧更新多次”警告是可怕的。 So I decided to create CustomGeometryReader所以我决定创建CustomGeometryReader

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM