布局系统:VStack垂直布局、HStack水平布局、ZStack层叠布局、Spacer弹性空间、Padding与Frame
布局,是SwiftUI的骨架。说白了,你写的每个界面,最终都要靠这些容器把元素「摆」到正确的位置上。我个人习惯把布局系统比作搭积木——VStack、HStack、ZStack就是三种不同的积木底座,Spacer是弹簧,Padding是缓冲垫,Frame是尺寸模具。搞懂它们,你就能搭出任何想要的界面。
VStack:垂直堆叠,从上往下
VStack是最常用的布局容器。它把子视图从上到下依次排列。我在项目中遇到过不少新手,一上来就疯狂嵌套VStack,结果界面卡得不行。其实VStack本身很轻量,但要注意别滥用。
VStack {
Text("第一行")
Text("第二行")
Text("第三行")
}
默认情况下,VStack会居中排列子视图。你可以通过alignment参数控制对齐方式,比如.leading左对齐、.trailing右对齐。间距用spacing控制,我一般习惯设成8或12,视觉上比较舒服。
HStack:水平排列,从左到右
HStack和VStack是对称的。它把子视图从左到右水平排列。你想想看,一个典型的表单——左边是标签,右边是输入框——用HStack就能轻松搞定。
HStack {
Text("用户名")
TextField("请输入", text: $username)
}
嗯,这里要注意:HStack里的子视图如果太多,可能会超出屏幕宽度。我曾经在做一个多标签筛选栏时,一口气塞了8个按钮进去,结果右边几个直接看不见了。解决方案是用.fixedSize()或者配合ScrollView滚动。
ZStack:层叠覆盖,前后关系
ZStack是三个容器里最特殊的。它不按顺序排列,而是把子视图一层层叠起来。就像Photoshop里的图层——后面的视图会盖住前面的。我经常用它来做背景叠加、遮罩效果、或者给卡片加阴影。
ZStack {
Color.blue
Text("Hello")
.foregroundColor(.white)
}
上面这段代码,蓝色背景上显示白色文字。ZStack默认也是居中对齐,你可以通过alignment参数调整。比如.topLeading就把所有子视图对齐到左上角。
我个人习惯用ZStack来做「徽标」效果——比如在头像右上角显示未读消息数。用ZStack把头像和数字叠在一起,再配合.offset()微调位置,非常方便。
Spacer:弹性空间,撑开布局
Spacer是SwiftUI里最神奇的「隐形弹簧」。它不显示任何内容,只负责占据剩余空间。你把它放在哪里,哪里就会被撑开。
HStack {
Text("左")
Spacer()
Text("右")
}
这段代码会把「左」推到最左边,「右」推到最右边。Spacer默认是均匀分配空间的,但你可以通过.frame(minWidth:)给它设置最小宽度。我在项目中遇到过一个问题:Spacer在ScrollView里会失效。为什么呢?因为ScrollView的内容宽度是无限的,Spacer永远占不满。解决办法是给ScrollView的内容设置一个明确的宽度。
Padding:内边距,呼吸空间
Padding给视图周围添加空白区域。说白了,就是让元素别贴边,留点呼吸空间。SwiftUI提供了默认的padding值(通常是16点),但你可以自定义。
Text("Hello")
.padding() // 默认四周16点
.padding(.leading) // 只加左边
.padding(20) // 四周20点
.padding([.top, .bottom], 10) // 只加上下
嗯,这里有个坑:padding的顺序会影响最终布局。如果你先设置padding再设置背景色,背景色会包含padding区域。反过来,先设置背景色再设置padding,背景色就不包含padding区域。我刚开始学SwiftUI时被这个坑过好几次。
Frame:尺寸模具,精确控制
Frame是SwiftUI里最强大的尺寸控制工具。它可以设置视图的宽度、高度,还能控制对齐方式和伸缩行为。我经常用Frame来做两件事:限制最大尺寸、设置固定比例。
Text("Hello")
.frame(width: 100, height: 50)
.frame(maxWidth: .infinity, alignment: .leading)
.frame(minHeight: 44, idealHeight: 50, maxHeight: 100)
第一行:固定宽100、高50。第二行:宽度撑满父视图,左对齐。第三行:最小高度44,理想高度50,最大高度100。Frame的参数组合非常灵活,我建议你多试试。
我曾经在做一个自适应卡片布局时,用Frame的aspectRatio参数实现了图片的等比缩放。比如设置.aspectRatio(16/9, contentMode: .fit),图片就会自动保持16:9比例,不会变形。
实战组合:一个典型的卡片布局
说了这么多,我们来个实战。下面这个卡片布局,用到了VStack、HStack、Spacer、Padding和Frame。你看看能不能一眼看出每部分的作用。
VStack(spacing: 12) {
HStack {
Text("标题")
.font(.headline)
Spacer()
Text("更多")
.font(.caption)
.foregroundColor(.blue)
}
.padding(.horizontal)
Text("这是一段描述文字,用来展示卡片的内容区域。")
.font(.body)
.padding(.horizontal)
HStack {
Text("2024-01-01")
.font(.caption)
.foregroundColor(.gray)
Spacer()
Text("阅读 123")
.font(.caption)
.foregroundColor(.gray)
}
.padding(.horizontal)
.padding(.bottom, 8)
}
.frame(maxWidth: .infinity)
.background(Color.white)
.cornerRadius(12)
.shadow(radius: 4)
.padding(.horizontal, 16)
这段代码生成一个标准的卡片:标题在左上角,「更多」在右上角,中间是描述文字,底部是日期和阅读数。整个卡片有圆角、阴影,左右留了16点边距。你想想看,如果没有Spacer,标题和「更多」就会挤在一起。没有Padding,文字就会贴边。没有Frame,卡片宽度就不会撑满。
alignmentGuide、GeometryReader等高级工具的理解——那是后面章节的内容了。