我们如何在 SwiftUI 中制作自定义 GeometryReader?
Posted
技术标签:
【中文标题】我们如何在 SwiftUI 中制作自定义 GeometryReader?【英文标题】:How can we make a Custom GeometryReader in SwiftUI? 【发布时间】:2021-11-10 19:09:47 【问题描述】:我想知道 GeometryReader 是如何工作的,我有兴趣构建一个自定义 GeometryReader 用于学习目的!
坦率地说,我认为我们在 body 中使用的每个视图都是 GeometryReaderView 的一种,不同之处在于它们不使用闭包来为我们发送代理,而且每个视图都回调它的代理会很烦人!因此苹果决定给 GeometryReader 提供几何阅读功能!所以这只是我的想法!
所以我正在寻找一种可能且更有可能的 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)
我还知道,视图的读取和报告代理不仅仅是视图的大小,它还与 CoordinateSpace、框架有关……但现在为了让事情更容易解决,我只是在研究大小!所以尺寸很重要!
正如我所说,我对使用 UIKit 或 UIViewRepresentable 来读取大小不感兴趣!可能苹果在掩护下使用类似的东西,也可能不使用! 我的目标是尝试用纯 SwiftUI 解决问题,或者你们中的一些人可能有一些关于 GeometryReader 源代码的很好的链接,以便阅读和学习它。
【问题讨论】:
GeometryReader
只会使用一些隐藏在 SwiftUI 中的东西。毕竟,SwiftUI 中的大多数组件只是UIView
s。 SwiftUI 将能够读取其中的frame
s、布局约束和其他任何相关内容。这样做是不合理的。
谢谢,这也是我的问题,我不确定 GeometryReader 是否真的使用UIView
!我的意思是我们都知道 SwiftUI 的大部分内容来自UIKit
,但我不知道是不是像你说的那样 100%。如果我们可以确定除了使用UIKit
之外别无他法,那么我称之为离目标更近一步。
SwiftUI 中的一些东西不是视图,但可能是CALayer
s。但同样,你不会知道它是真的是CALayer
还是只是CALayer
之类的东西,所以你很可能无法可靠地做你想做的事。
【参考方案1】:
好的,SwiftUI 中有几个工具提供了对视图大小的访问(当然,GeometryReader
除外)。
问题当然是将那个大小值转移到视图构建阶段,因为只有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)
)
【讨论】:
感谢您的回答,坦率地说,我知道尝试使用 Shape 来读取尺寸!但我并没有尝试这样做,因为在那种情况下,我的问题仍然没有答案!更多信息:我不知道geometryReader 如何在掩护下工作,与Shape 相同的问题,我不知道Shape 如何在掩护下工作!我希望我能说出我的意思!问题是geometryReader 或Shape 如何测量可用空间!我现在只是对geometryReader 提出疑问,但是一旦我们知道它是如何工作的,它就可以用来了解Shape 的工作原理。【参考方案2】:每当我需要知道视图的框架时,GeometryReader 都会变得不必要地复杂,并且“绑定首选项尝试每帧更新多次”警告非常可怕。所以我决定创建CustomGeometryReader
【讨论】:
以上是关于我们如何在 SwiftUI 中制作自定义 GeometryReader?的主要内容,如果未能解决你的问题,请参考以下文章