SwiftUI

SwiftUI GeometryReader的一点理解

转载:https://www.wwt.com/article/using-geometryreader-to-build-relative-layouts

虽然大多数时候开发人员可以利用各种Stack并允许 SwiftUI 调整自己的布局,但有时我们希望对视图在屏幕上的显示方式有更多的控制。我们可以通过使用 GeometryReader 来实现这个目标。

例如,假设我们有一个 Text 视图并给它一个黄色背景,以便我们可以看到我们视图的框架。默认情况下,SwiftUI 会将 Text 放置在中心,并且只占用视图所需的空间。

如果我们希望视图占据整个宽度而不管它包含多少文本怎么办?这就是 GeometryReader 发挥作用的地方。根据 Apple 的说法,GeometryReader 是“一个容器视图,它将其内容定义为其自身大小和坐标空间的函数。”

与 SwiftUI 中的大多数内容一样,GeometryReader 是一个视图,在子视图中可以拿到父视图分配的最大尺寸。因此,如果我们希望我们的子视图覆盖屏幕宽度的一半,我们可以将子视图放入 GeometryReader 并将其宽度设置为最大分配宽度的一半。

你会注意到子视图现在是屏幕宽度的一半,但它也移动到顶部前缘而不是居中。GeometryReader 调整自身以占用尽可能多的可用空间。

通过将 GeometryReader 背景颜色设置为蓝色,我们可以更清楚地看到这一点。GeometryReader 的蓝色背景覆盖了安全区域内的所有内容(父视图尽可能提供大的空间给子视图),但子视图 Text 本身是 GeometryReader 区域内的一个较小的视图。记住GeometryReader本身也是一个view,所以他会有自己的Frame(width,height),会占用布局空间。

这可能是所需的结果,但出于示例目的,我们希望保持文本居中。我们有几个选项可以实现这一点,一个是将 Text 包裹在stack和space的组合中,或者调整stack的frame以填充 GeometryReader 占用的全部空间。但是由于我们已经在使用 GeometryReader,我们可以访问关于我们子视图在父视图的信息。 

在 GeometryReader 中,我们可以简单地使用geometry.frame(in: .local)到 (midX, midY)添加一个位置。位置会将视图的中心放在其父级内的指定坐标处。

GeometryReader 的一个好处是,因为它会调整以填充所有可用空间,它的子视图将根据 GeometryReader 本身有多少屏幕空间进行调整。如果我们在 GeometryReader 之外添加一个 HStack 并添加一个前面的 Image 视图,我们的 Text 将减小其宽度以保持其父视图 GeometryReader 宽度的一半。

因为 GeometryReader 是一个 SwiftUI 视图,它将有关其坐标空间的信息传递给其子视图,所以在使用相对定位和大小布局视图时,它为我们提供了更多控制权。

一个例子,大家可以加深理解下:

struct ContentView: View {
    var body: some View {
        VStack {
            GeometryReader { geometry in
                Text("A:\(geometry.size.width)_\(geometry.size.height)")
                    .padding()
            }.background(Color.red)
            GeometryReader { geometry in
                Text("B:\(geometry.size.width)_\(geometry.size.height)")
                    .padding()
            }.background(Color.blue)
        }
     
    }
}
struct ContentView: View {
    var body: some View {
        VStack {
            GeometryReader { geometry in
                Text("A:\(geometry.size.width)_\(geometry.size.height)")
                    .padding()
            }.background(Color.red)
        }

    }
}