SwiftUI 布局:视图的绝对定位
SwiftUI为我们提供了两种定位视图的方法:使用 position()
的绝对位置和使用 offset()
的相对位置。它们看起来可能很相似,但一旦了解了 SwiftUI 如何将视图放置在Frame内,position()
和 offset()
之间的根本区别就变得更清楚了。
默认的 SwiftUI 模板如下所示:
struct ContentView: View {
var body: some View {
Text("Hello, world!")
}
}
SwiftUI为 ContentView
提供了全部可用空间,而 ContentView
又将其传递给文本视图。文本视图自动使用的空间仅限于其文本所需的空间,因此它将这些空间传递回ContentView
,后者始终与正文大小完全相同(因此它直接适合于文本)。因此,SwiftUI将ContentView
集中在可用空间中,从用户的角度来看,这是将文本放在中心的地方。
如果要绝对定位 SwiftUI 视图,应使用position()
修饰符,如下所示:
Text("Hello, world!")
.position(x: 100, y: 100)
这将把文本视图定位在其父视图中的 x:100 y:100。现在,为了真正了解这里发生了什么,我希望您添加一个背景色:
Text("Hello, world!")
.background(Color.red)
.position(x: 100, y: 100)
你会看到文本周围有一个紧密贴合的红色背景。现在尝试将background()
修饰符移到 position()
修饰符下方,如下所示:
Text("Hello, world!")
.position(x: 100, y: 100)
.background(Color.red)
现在您将看到文本在同一位置,但整个Safe Area 是红色的。
要了解这里发生了什么,您需要记住SwiftUI的三步布局过程:
- 父视图提供一个大小并询问其子视图的大小。
- 子视图根据自己的信息,它会选择自己的尺寸,而父视图必须尊重这个选择。
- 然后父视图在其坐标空间中定位子视图。
所以,父母负责定位孩子,而不是孩子自己。这导致了一个问题,因为我们刚刚告诉我们的文本视图处于一个准确的位置——SwiftUI如何解决这个问题?
这个问题的答案也是为什么我们的background()
使整个安全区域变为红色:当我们使用position()
时,我们会得到一个新的视图,它会占用所有可用的空间,这样它就可以将它的子视图(文本)放在正确的位置。
当我们使用文本、位置和背景时,位置将占用所有可用空间,以便它能够正确地定位其文本,然后背景将为其自身使用该大小。当我们使用文本、背景和位置时,背景将使用文本大小作为其大小,然后位置将占用所有可用空间并将背景放置在正确的位置。
在前面讨论offset()
修饰符时,我说“如果偏移一些文本,它的原始尺寸实际上没有改变,即使结果视图是在不同的位置呈现的。”记住这一点,请尝试运行以下代码:
var body: some View {
Text("Hello, world!")
.offset(x: 100, y: 100)
.background(Color.red)
}
你会看到文本出现在一个地方,背景出现在另一个地方。我要解释为什么会这样,但首先我希望你自己考虑一下,因为如果你理解了这一点,那么你就真正理解了SwiftUI的布局系统是如何工作的。
当我们使用offset()
修饰符时,我们改变了一个视图应该呈现的位置,而不实际改变它的基本几何体。这意味着当我们随后应用background()
时,它使用文本的原始位置,而不是偏移量。如果移动修饰符的顺序,使background()
在offset()
之前,那么事情会更像您预期的那样,再次表明修饰符顺序很重要。