SwiftUI 入门：布局基础

基础布局容器

Stack

在 SwiftUI 中，Stack 是一种基本的视图容器，可以用来排列和布局其他视图。
在 Stack 中，所有的子视图都是在一个水平或垂直方向上排列的，你可以使用 HStack 创建水平方向的 Stack，使用 VStack 创建垂直方向的 Stack，或者使用 ZStack 创建一个带有层次关系的 Stack。

VStack

在 SwiftUI 中，VStack 是一种基本的视图容器，用于在垂直方向上排列其他视图。
VStack 可以包含任意数量的子视图（其实是有上限的，但可以通过 Group 规避这个限制），这些子视图将按照从上到下的顺序排列。

以下是一个简单的 VStack 示例：

Copy
struct ContentView: View {
var body: some View {
VStack {
Text("First")
Text("Second")
Text("Third")
}
}
}

在这个示例中，VStack 包含了三个 Text 视图，它们被按照从上到下的顺序排列。

除了基本的布局外，VStack 还支持许多其他的选项和修饰符，以帮助你自定义视图的外观和行为。
以下是一些常用的选项和修饰符：

alignment：用于指定子视图在垂直方向上的对齐方式。默认情况下，子视图将在垂直中心对齐，但你可以使用 alignment 选项来指定其他的对齐方式，例如 .leading、.trailing、.top、.bottom 等等。

spacing：用于指定子视图之间的间距。默认情况下，子视图之间没有间距，但你可以使用 spacing 选项来添加间距，例如 VStack(spacing: 10) 将在每个子视图之间添加 10 个点的间距。

padding：用于指定 VStack 的内边距。默认情况下，VStack 没有内边距，但你可以使用 padding 选项来添加内边距，例如 VStack().padding(10) 将在 VStack 外部添加 10 个点的内边距。

需要注意的是，当你在 VStack 中包含多个视图时，它们的大小和位置可能会受到其他视图的影响。为了避免这种情况，你可以使用 Spacer 视图来占用额外的空间，或者使用 GeometryReader 来获取父视图的大小，并根据需要对子视图进行调整。

HStack

和 VStack 相似，差异仅仅表现在方向上。
以下是一个简单的 HStack 示例：

Copy
struct ContentView: View {
var body: some View {
HStack {
Text("First")
Text("Second")
Text("Third")
}
}
}

在这个示例中，HStack 包含了三个 Text 视图，它们被按照从左到右的顺序排列。

HStack 常用的选项和修饰符和 VStack 类似：

alignment：用于指定子视图在水平方向上的对齐方式。默认情况下，子视图将在水平中心对齐，但你可以使用 alignment 选项来指定其他的对齐方式，例如 .leading、.trailing、.top、.bottom 等等。

spacing：用于指定子视图之间的间距。默认情况下，子视图之间没有间距，但你可以使用 spacing 选项来添加间距，例如 HStack(spacing: 10) 将在每个子视图之间添加 10 个点的间距。

padding：用于指定 HStack 的内边距。默认情况下，HStack 没有内边距，但你可以使用 padding 选项来添加内边距，例如 HStack().padding(10) 将在 HStack 外部添加 10 个点的内边距。

ZStack

在 SwiftUI 中，ZStack 是一种基本的视图容器，用于在垂直方向上堆叠其他视图。ZStack 可以包含任意数量的子视图，这些子视图将按照从前到后的顺序堆叠，类似于图层。以下是一个简单的 ZStack 示例：

Copy
struct ContentView: View {
var body: some View {
ZStack {
Image("background")
Text("Hello, World!")
}
}
}

在这个示例中，ZStack 包含了一个 Image 视图和一个 Text 视图，它们被堆叠在一起。由于 Text 视图在 Image 视图的前面，因此它会显示在最上面。

除了基本的堆叠外，ZStack 还支持许多其他的选项和修饰符，以帮助你自定义视图的外观和行为。以下是一些常用的选项和修饰符：

alignment：用于指定子视图在垂直方向上的对齐方式。默认情况下，子视图将在垂直中心对齐，但你可以使用 alignment 选项来指定其他的对齐方式，例如 .leading、.trailing、.top、.bottom 等等。

background：用于指定 ZStack 的背景视图。默认情况下，ZStack 没有背景视图，但你可以使用 background 选项来添加背景视图，例如 ZStack().background(Color.blue) 将在 ZStack 后面添加一个蓝色的背景。

overlay：用于在 ZStack 上添加覆盖视图。默认情况下，ZStack 没有覆盖视图，但你可以使用 overlay 选项来添加覆盖视图，例如 ZStack().overlay(Text("Overlay")) 将在 ZStack 上方添加一个覆盖视图。

需要注意的是，当你在 ZStack 中包含多个视图时，它们的大小和位置可能会受到其他视图的影响。为了避免这种情况，你可以使用 Spacer 视图来占用额外的空间，或者使用 GeometryReader 来获取父视图的大小，并根据需要对子视图进行调整。

LazyVStack

LazyVStack 是 SwiftUI 中用于垂直布局的一种视图。LazyVStack 类似于 VStack，但具有更高的性能和更少的内存使用，因为它仅在需要时才加载和显示其子视图。LazyVStack 通常用于显示大量的子视图，如列表和网格。

以下是创建 LazyVStack 的基本语法：

Copy
LazyVStack(alignment: HorizontalAlignment = .center, spacing: CGFloat? = nil, pinnedViews: PinnedScrollableViews = .init(), @ViewBuilder content: () -> Content)

其中，alignment参数用于指定子视图在垂直轴上的对齐方式（如.top、.center、.bottom等），spacing参数用于指定子视图之间的间距，pinnedViews参数用于指定要固定在ScrollView顶部或底部的视图，content参数是一个闭包，用于返回要在LazyVStack中显示的内容。

例如，下面的代码创建了一个简单的LazyVStack，其中包含 10 个文本视图：

Copy
LazyVStack {
ForEach(1...10, id: \.self) { index in
Text("Item \(index)")
}
}

在这个例子中，我们使用ForEach循环来创建 10 个文本视图，并将它们添加到LazyVStack中。由于LazyVStack是 “惰性” 的，它只会在需要时动态加载和显示每个文本视图，这可以提高性能并减少内存使用。

LazyHStack

LazyHStack 是 SwiftUI 中的一个视图容器，用于在水平方向上排列一组视图。与 HStack 不同的是，LazyHStack 会在需要时才实例化它的子视图，这可以提高性能并减少内存使用量。

以下是 LazyHStack 的一些常见用法：

排列一组视图：当你需要在水平方向上排列一组视图时，可以使用 LazyHStack 来将它们放置在一个容器中。这使得可以更轻松地布局视图，并在需要时进行懒加载，从而提高性能和内存使用效率。

举个例子，如果你需要在一个视图中排列一组按钮，可以使用 LazyHStack 来将它们放置在一个水平容器中，并在需要时进行懒加载。

Copy
LazyHStack {
Button("Button 1") {
// do something
}
Button("Button 2") {
// do something
}
Button("Button 3") {
// do something
}
}

支持大量视图的布局：当你需要在一个视图中放置大量视图时，可以使用 LazyHStack 来进行懒加载，从而提高性能和内存使用效率。这对于需要展示大量数据的应用程序非常有用，如社交媒体应用程序、新闻应用程序等。

举个例子，如果你需要在一个视图中显示一组图片，可以使用 LazyHStack 来将它们放置在一个水平容器中，并在需要时进行懒加载。

Copy
LazyHStack {
ForEach(0..<100) { index in
Image("image\(index)")
.resizable()
.frame(width: 50, height: 50)
}
}

总之，LazyHStack 可以帮助你在 SwiftUI 中更轻松地布局和管理视图，并提高性能和内存使用效率。如果你需要在水平方向上排列一组视图，并且希望在需要时进行懒加载，那么 LazyHStack 是一个非常实用的视图容器。

Grid

在 SwiftUI 中，可以使用 LazyVGrid、LazyHGrid 和 LazyVGrid 三种视图容器来创建网格布局。这些容器提供了一种方便的方式来排列和布局许多视图，可以让你轻松地创建表格、照片墙、电影海报和其他复杂的布局。

以下是一些 SwiftUI 中使用网格布局的常见方法和示例：

LazyVGrid

垂直网格布局：可以使用 LazyVGrid 容器来创建垂直方向上的网格布局。在 LazyVGrid 中，你可以指定列数、行之间的间距和单元格之间的间距。

Copy
struct ContentView: View {
let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]

var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 16) {
ForEach(0 ..< 10) { index in
Text("Item \(index)")
.frame(height: 100)
}
}
.padding()
}
}
}

LazyHGrid

水平网格布局：可以使用 LazyHGrid 容器来创建水平方向上的网格布局。在 LazyHGrid 中，你可以指定行数、列之间的间距和单元格之间的间距。

Copy
struct ContentView: View {
let rows = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]

var body: some View {
ScrollView(.horizontal) {
LazyHGrid(rows: rows, spacing: 16) {
ForEach(0 ..< 10) { index in
Text("Item \(index)")
.frame(width: 200, height: 100)
}
}
.padding()
}
}
}

LazyGrid

自定义网格布局：可以使用 LazyGrid 容器来创建自定义网格布局。在 LazyGrid 中，你可以指定行数、列数、行之间的间距和列之间的间距，并自定义每个单元格的大小和位置。

Copy
struct ContentView: View {
let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]

var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 16) {
ForEach(0 ..< 10) { index in
Text("Item \(index)")
.frame(height: CGFloat.random(in: 50...200))
.background(Color.blue)
.cornerRadius(8)
.overlay(
Text("\(index)")
.foregroundColor(.white)
)
}
}
.padding()
}
}
}

ScrollView

基本用法

ScrollView 是 SwiftUI 中用于显示可滚动内容的一种视图。ScrollView 可以垂直或水平滚动，并支持滚动手势和滚动条。ScrollView 可以用于显示各种类型的内容，如文本、图像、列表等等。

以下是创建 ScrollView 的基本语法：

Copy
struct ContentView: View {
var body: some View {
ScrollView {
VStack {
ForEach(0..<50) { index in
Text("Row \(index)")
}
}
}
}
}

在 ScrollView 中，我们可以添加任意数量的视图，包括文本视图、图像视图、列表视图等等。ScrollView 会自动将这些视图包裹在一个可滚动的容器中，并根据内容自动调整它们的大小和位置。

例如，下面的代码创建了一个简单的 ScrollView，其中包含一个文本视图：

Copy
ScrollView {
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vel ante ac lorem bibendum semper. Nulla facilisi. Sed quis risus nec risus bibendum tempus. Nam luctus hendrerit eros, a tempor augue pharetra vel. Donec at risus vitae velit malesuada egestas.")
}

在这个例子中，ScrollView 会自动将文本视图包裹在一个可滚动的容器中，并根据文本的长度自动调整其大小和位置。用户可以通过手势滚动文本视图，或使用滚动条控制滚动位置。

常用修饰符

以下是 ScrollView 的一些常用修饰符：

scrollIndicatorInsets(_ :): 用于设置滚动指示器的边距。这个修饰符接受一个 EdgeInsets 类型的参数，用于指定顶部、底部、左侧和右侧的边距。

showsIndicators(_ :): 用于控制是否显示滚动指示器。这个修饰符接受一个布尔值类型的参数，默认值为 true，表示显示滚动指示器。如果将其设置为 false，则不会显示滚动指示器。

onOffsetChange(_ :): 用于在 ScrollView 滚动时执行自定义操作。这个修饰符接受一个闭包类型的参数，其中包含当前偏移量的值。可以使用这个修饰符来实现自定义滚动行为，例如在滚动到特定位置时执行某些操作。

contentInsets(_ :): 用于设置 ScrollView 内容的边距。这个修饰符接受一个 EdgeInsets 类型的参数，用于指定顶部、底部、左侧和右侧的边距。

contentOffset(_ :): 用于设置 ScrollView 的初始偏移量。这个修饰符接受一个 CGPoint 类型的参数，用于指定 ScrollView 的初始偏移量。

background(_ :): 用于设置 ScrollView 的背景。这个修饰符接受一个 View 类型的参数，用于指定 ScrollView 的背景视图。

overlay(_ :): 用于在 ScrollView 上添加覆盖视图。这个修饰符接受一个 View 类型的参数，用于指定要添加到 ScrollView 上的覆盖视图。

ignoresSafeArea(_ edges: Edge.Set): 用于控制 ScrollView 是否忽略安全区域。这个修饰符接受一个 Edge.Set 类型的参数，用于指定要忽略的安全区域边缘。

content(_ :): 用于指定 ScrollView 的内容视图。这个修饰符接受一个 View 类型的参数，用于指定 ScrollView 的内容视图。

Spacer

Spacer 是 SwiftUI 中的一个视图修饰符，用于在视图中创建一个占位符，以便在布局过程中占用空间。当你使用 Spacer 时，它会自动填充剩余的可用空间，从而将视图推动到视图容器的边缘。

以下是 Spacer 的一些常见用法：

在 VStack 或 HStack 中使用 Spacer：当你将 Spacer 放置在 VStack 或 HStack 中时，它会自动将其前面的视图推到容器的顶部或左侧，并将其后面的视图推到容器的底部或右侧。这使得可以在容器中创建自适应的布局。

举个例子，如果你想在 VStack 中创建一个具有固定高度的标题和一个自适应高度的文本视图，你可以在它们之间添加一个 Spacer。这将会使文本视图自动填充剩余的可用空间，从而将其推到容器的底部。

Copy
VStack {
Text("Title")
.font(.largeTitle)
.frame(height: 100)
Spacer()
Text("This is a long text that should wrap over several lines and fill the remaining space in the container.")
}

在 ZStack 中使用 Spacer：当你将 Spacer 放置在 ZStack 中时，它会将其前面的视图推到前面，将其后面的视图推到后面，并自动占用剩余的可用空间。这使得可以在 ZStack 中创建自适应的布局。

举个例子，如果你想在 ZStack 中创建一个具有固定大小的圆形图像和一个自适应大小的矩形视图，你可以在它们之间添加一个 Spacer。这将会使矩形视图自动填充剩余的可用空间，从而将其推到圆形图像的周围。

Copy
ZStack {
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
Spacer()
Rectangle()
.fill(Color.red)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}

总之，Spacer 可以帮助你在 SwiftUI 中创建自适应布局，使得视图可以自动填充剩余的可用空间，从而实现更加灵活和动态的布局效果。

Group

GGroup 是 SwiftUI 中的一个容器视图，用于将多个视图组合在一起，并对它们进行分组。它不会在视图层次结构中创建任何新的视图层次关系，而只是将其内部的视图作为一组视图进行处理。

以下是 Group 的一些常见用法：

将多个视图组合在一起：当你需要将多个视图组合在一起时，可以使用 Group 来将它们包装在一个容器中。这使得可以在代码中更好地组织视图，并使其更易于阅读和维护。

举个例子，如果你需要在一个视图中显示多个按钮，可以将这些按钮放在一个 Group 中，这样它们就可以作为一个整体处理，并可以在需要时轻松地进行样式和布局更改。

Copy
Group {
Button("Button 1") {
// do something
}
Button("Button 2") {
// do something
}
Button("Button 3") {
// do something
}
}

对视图进行分组：当你需要对视图进行分组时，可以使用 Group 来将它们包装在一个容器中。这使得可以轻松地将相似的视图组织在一起，并将其作为一个整体进行处理。

举个例子，如果你需要在一个视图中显示多个文本标签，可以将它们分组在一个 Group 中，并为该组添加样式和布局属性。这样可以更好地组织代码，并使其更易于维护。

Copy
Group {
Text("Label 1")
Text("Label 2")
Text("Label 3")
}
.font(.largeTitle)
.padding()

总之，Group 可以帮助你在 SwiftUI 中更好地组织和管理视图，并使其更易于阅读和维护。它是一个非常简单但实用的容器视图，可以使代码更加简洁和可读。

嵌套布局

在 SwiftUI 中，可以通过嵌套一个视图容器来创建复杂的布局。这允许你使用多个容器，每个容器都负责管理一组视图。这样可以更轻松地组合和布局视图，并在需要时进行懒加载，以提高性能和内存使用效率。

以下是一些 SwiftUI 中常用的嵌套布局的示例：

垂直和水平布局的组合：可以使用 VStack 和 HStack 的组合来实现垂直和水平布局的混合。这对于需要在水平和垂直方向上排列视图的应用程序非常有用，如表单、工具栏等。

Copy
VStack {
HStack {
Text("First")
Text("Second")
}
HStack {
Text("Third")
Text("Fourth")
}
}

嵌套的列表：可以将列表视图嵌套在另一个列表或视图容器中，以创建更复杂的布局。这对于需要显示层次结构数据的应用程序非常有用，如目录、导航等。

Copy
List {
Section(header: Text("Section 1")) {
Text("Item 1")
Text("Item 2")
}
Section(header: Text("Section 2")) {
List {
Text("Nested Item 1")
Text("Nested Item 2")
}
}
}

嵌套的滚动视图：可以将滚动视图嵌套在另一个滚动视图或视图容器中，以创建更复杂的滚动布局。这对于需要显示大量数据的应用程序非常有用，如新闻、社交媒体等。

Copy
ScrollView {
VStack {
Image("header-image")
.resizable()
.frame(height: 200)
VStack {
Text("Title")
.font(.largeTitle)
Text("Subtitle")
.font(.headline)
.foregroundColor(.gray)
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, ligula id gravida rutrum, sem urna volutpat purus, sit amet suscipit ex odio vel arcu. Nulla cursus sollicitudin massa. Etiam quis ex at odio eleifend mollis. Fusce accumsan turpis ac ipsum ultrices, nec lobortis dolor consectetur. Donec sit amet bibendum mi, id vestibulum justo. Sed eget dictum orci. Donec vitae quam ut eros rhoncus faucibus.")
.font(.body)
}
}
}

布局修饰符

在 SwiftUI 中，布局修饰符可以用于在视图周围添加内边距、背景、覆盖图像等。以下是常用的布局修饰符：

padding(_:)：在视图周围添加内边距。例如，Text("Hello World").padding() 将在 Text 视图周围添加默认内边距。

background(_:)：为视图添加背景。例如，Text("Hello World").background(Color.blue) 将为 Text 视图添加蓝色背景。

border(_:width:)：为视图添加边框。例如，Text("Hello World").border(Color.gray, width: 1) 将为 Text 视图添加灰色边框，宽度为 1。

cornerRadius(_:)：为视图添加圆角。例如，Text("Hello World").cornerRadius(10) 将为 Text 视图添加 10 个点的圆角。

shadow(_:)：为视图添加阴影。例如，Text("Hello World").shadow(color: .gray, radius: 2) 将为 Text 视图添加灰色阴影，半径为 2。

overlay(_:)：在视图上方添加覆盖图像。例如，Text("Hello World").overlay(Image(systemName: "star")) 将在 Text 视图上方添加一个星形图像。

frame(width:height:alignment:)：为视图指定框架大小和对齐方式。例如，Text("Hello World").frame(width: 100, height: 50, alignment: .center) 将为 Text 视图指定一个宽度为 100、高度为 50、水平和垂直居中对齐的框架。

aspectRatio(_:contentMode:)：为视图指定宽高比和内容模式。例如，Image("photo").aspectRatio(1.5, contentMode: .fit) 将为 Image 视图指定一个宽高比为 1.5，并将图像内容缩放以适应视图大小。

alignmentGuide(_:computeValue:)：允许你定义自定义的对齐指南，以便在布局中使用。这个修饰符接受两个参数：一个对齐标识符和一个计算函数。例如，VStack(alignment: .leading) { ... }.alignmentGuide(.leading) { d in d[.trailing] } 可以将 .leading 对齐指南的位置设置为其子视图的 .trailing。

fixedSize()：指定视图的大小为其内容大小。例如，Text("Hello World").fixedSize() 将为 Text 视图指定一个大小，该大小等于 Text 视图内容的大小。

layoutPriority(_:)：指定视图的布局优先级。布局优先级用于确定哪个视图将优先布局。例如，VStack { Text("Hello").layoutPriority(1); Text("World").layoutPriority(2) } 将确保 Text("World") 视图具有更高的布局优先级，并且将优先布局。

offset(_:)：在视图的位置上添加偏移量。例如，Text("Hello World").offset(x: 10, y: 20) 将在 Text 视图的位置上添加 x 轴偏移量为 10，y 轴偏移量为 20。

rotationEffect(_:anchor:)：为视图添加旋转效果。例如，Text("Hello World").rotationEffect(.degrees(45)) 将为 Text 视图添加 45 度的旋转效果。

scaleEffect(_:anchor:)：为视图添加缩放效果。例如，Text("Hello World").scaleEffect(2) 将为 Text 视图添加两倍大小的缩放效果。

clipped()：将视图修剪为其父视图的边界框内。例如，Image("photo").clipped() 将将图像修剪为其父视图的边界框内。

布局优先级

在 SwiftUI 中，布局优先级用于决定视图在布局中的相对位置。布局优先级越高的视图将在布局时优先考虑，并优先分配可用空间。如果所有视图的布局优先级相同，则它们将平均分配可用空间。

你可以使用 layoutPriority(_:) 修饰符为视图设置布局优先级。这个修饰符接受一个浮点数值作为参数，表示视图的布局优先级。默认情况下，所有视图的布局优先级都是 0。

例如，你可以使用以下代码为两个文本视图设置布局优先级：

Copy
VStack {
Text("First").layoutPriority(1)
Text("Second").layoutPriority(2)
}

在上面的代码中，第二个文本视图具有更高的布局优先级，因此它将首先分配可用的空间。如果它需要更多的空间，它将优先考虑并尽可能多地分配可用的空间。而第一个文本视图的布局优先级较低，因此它将尽可能少地分配可用的空间。

需要注意的是，布局优先级只能用于决定子视图在父视图中的相对位置，而不能用于决定子视图在屏幕中的绝对位置。如果你想要更精细的布局控制，可以考虑使用其他布局修饰符，如 frame、padding、offset 等。

自动布局

在 SwiftUI 中，自动布局是通过使用一种称为 “声明式布局” 的方法来实现的。这种方法允许你通过声明视图的期望行为，而不是直接操作布局，来控制视图的布局。这种方法非常类似于使用 HTML 和 CSS 创建 Web 页面的方式。

SwiftUI 中的自动布局是基于一组内置的布局系统，包括 VStack、HStack、ZStack、List、ScrollView 等。这些布局系统可以嵌套使用，并且可以通过添加修饰符来自定义它们的行为。

例如，以下代码使用 VStack 和 HStack 来创建一个简单的布局：

Copy
VStack {
HStack {
Text("Hello")
Text("World")
}
Text("How are you?")
}

在上面的代码中，VStack 和 HStack 分别表示垂直和水平方向的布局，因此它们可以嵌套使用来创建更复杂的布局。在这个例子中，HStack 包含两个文本视图，这两个文本视图在水平方向上并排排列。VStack 包含一个 HStack 和一个文本视图，这些视图在垂直方向上排列。因此，整个布局包含三个文本视图，其中两个在水平方向上并排排列，而第三个在垂直方向上排列。

需要注意的是，SwiftUI 中的自动布局是响应式的，这意味着视图将根据其内容和环境自动调整大小和位置。例如，当设备的屏幕方向发生变化时，视图的布局将自动更新以适应新的方向。