第4章:基础布局系统
![]()
示例代码都放这里啦,有需要的可以下载学习。swiftUIDemo
4.1 垂直布局:VStack
VStack 介绍
VStack 是 SwiftUI 中用于垂直堆叠视图的容器,它会将子视图按垂直方向排列。VStack 是构建垂直布局的基础组件,适用于需要从上到下排列的界面元素。
基本用法
// 基本垂直栈
VStack {
Text("第一行")
Text("第二行")
Text("第三行")
}
// 带间距和对齐的垂直栈
VStack(alignment: .leading, spacing: 20) {
Text("左对齐")
Text("第二行")
Text("第三行")
}
.padding()
对齐方式
VStack 提供了三种主要的对齐方式:
-
.leading:左对齐 -
.center:居中对齐(默认) -
.trailing:右对齐 -
.top、.bottom:在嵌套布局中使用
// 不同对齐方式
VStack(alignment: .leading) {
Text("左对齐")
Text("这是一行更长的文本")
Text("短文本")
}
.padding()
VStack(alignment: .center) {
Text("居中对齐")
Text("这是一行更长的文本")
Text("短文本")
}
.padding()
VStack(alignment: .trailing) {
Text("右对齐")
Text("这是一行更长的文本")
Text("短文本")
}
.padding()
嵌套 VStack
VStack 可以嵌套使用,创建更复杂的布局结构。
// 嵌套垂直栈
VStack(spacing: 10) {
Text("标题")
.font(.headline)
VStack(alignment: .leading, spacing: 8) {
Text("项目 1")
Text("项目 2")
Text("项目 3")
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
Button("确认") {}
}
.padding()
适用场景
- 表单布局:从上到下排列的输入字段
- 列表项:垂直排列的内容块
- 页面结构:标题、内容、按钮的垂直布局
- 卡片式布局:垂直堆叠的信息卡片
性能考虑
- VStack 会根据子视图的大小自动调整高度
- 对于大量子视图,考虑使用
LazyVStack来提高性能 - 避免过深的嵌套,可能会影响渲染性能
4.2 水平布局:HStack
HStack 介绍
HStack 是 SwiftUI 中用于水平堆叠视图的容器,它会将子视图按水平方向排列。HStack 是构建水平布局的基础组件,适用于需要从左到右排列的界面元素。
基本用法
// 基本水平栈
HStack {
Text("左侧")
Text("中间")
Text("右侧")
}
// 带间距和对齐的水平栈
HStack(alignment: .top, spacing: 20) {
Text("顶部对齐")
Text("这是一行\n多行文本")
Text("短文本")
}
.padding()
对齐方式
HStack 提供了三种主要的对齐方式:
-
.top:顶部对齐 -
.center:居中对齐(默认) -
.bottom:底部对齐 -
.leading、.trailing:在嵌套布局中使用
// 不同对齐方式
HStack(alignment: .top) {
Text("顶部对齐")
Text("这是一行\n多行文本")
Text("短文本")
}
.padding()
HStack(alignment: .center) {
Text("居中对齐")
Text("这是一行\n多行文本")
Text("短文本")
}
.padding()
HStack(alignment: .bottom) {
Text("底部对齐")
Text("这是一行\n多行文本")
Text("短文本")
}
.padding()
空间分配
HStack 可以使用 Spacer 来分配空间,实现更灵活的布局。
// 空间分配
HStack {
Text("左侧")
Spacer() // 占据剩余空间
Text("右侧")
}
.padding()
// 带比例的空间分配
HStack {
Text("1/4 宽度")
.frame(maxWidth: .infinity)
Spacer()
Text("1/4 宽度")
.frame(maxWidth: .infinity)
Spacer()
Text("1/4 宽度")
.frame(maxWidth: .infinity)
Spacer()
Text("1/4 宽度")
.frame(maxWidth: .infinity)
}
.padding()
适用场景
- 工具栏:水平排列的操作按钮
- 列表项内容:左侧图标、中间文本、右侧箭头
- 表单行:标签和输入框的水平排列
- 导航栏:左侧返回按钮、中间标题、右侧操作按钮
性能考虑
- HStack 会根据子视图的大小自动调整宽度
- 对于大量子视图,考虑使用
LazyHStack来提高性能 - 注意水平空间不足时的布局行为,可能需要使用
ScrollView
4.3 层叠布局:ZStack
ZStack 介绍
ZStack 是 SwiftUI 中用于层叠视图的容器,它会将子视图按层叠方式排列,后面的视图会覆盖前面的视图。ZStack 是构建叠加效果的基础组件,适用于需要层级关系的界面元素。
基本用法
// 基本层叠
ZStack {
Color.blue // 背景
Text("前景文本")
.foregroundStyle(.white)
.font(.largeTitle)
}
.frame(height: 200)
// 多层叠
ZStack {
// 底层
Rectangle()
.fill(Color.yellow)
.frame(width: 200, height: 200)
// 中层
Circle()
.fill(Color.green)
.frame(width: 150, height: 150)
// 顶层
Text("ZStack")
.font(.headline)
}
对齐方式
ZStack 提供了多种对齐方式,可以精确控制子视图的位置:
-
.topLeading、.top、.topTrailing -
.leading、.center、.trailing -
.bottomLeading、.bottom、.bottomTrailing
// 不同对齐方式
ZStack(alignment: .topLeading) {
Rectangle()
.fill(Color.gray.opacity(0.2))
.frame(width: 300, height: 200)
Text("左上角")
.padding(10)
}
ZStack(alignment: .center) {
Rectangle()
.fill(Color.gray.opacity(0.2))
.frame(width: 300, height: 200)
Text("居中")
}
ZStack(alignment: .bottomTrailing) {
Rectangle()
.fill(Color.gray.opacity(0.2))
.frame(width: 300, height: 200)
Text("右下角")
.padding(10)
}
实际应用
// 带徽章的图标
ZStack(alignment: .topTrailing) {
Image(systemName: "bell")
.font(.system(size: 24))
Circle()
.fill(Color.red)
.frame(width: 16, height: 16)
.overlay {
Text("3")
.font(.system(size: 10))
.foregroundStyle(.white)
}
.offset(x: 4, y: -4)
}
// 卡片覆盖效果
ZStack {
RoundedRectangle(cornerRadius: 12)
.fill(Color.white)
.shadow(radius: 4)
.frame(width: 300, height: 200)
VStack {
Text("卡片标题")
.font(.headline)
Text("卡片内容")
.foregroundStyle(.secondary)
}
.padding()
// 右上角标签
Text("NEW")
.font(.caption)
.foregroundStyle(.white)
.padding(4)
.background(Color.blue)
.cornerRadius(4)
.offset(x: 45, y: -10)
}
适用场景
- 带背景的文本:文本叠加在背景之上
- 徽章效果:通知图标上的数字徽章
- 卡片布局:带有覆盖元素的信息卡片
- 复杂 UI 组件:需要多层叠加的自定义控件
- 模态视图:半透明覆盖层
性能考虑
- ZStack 会按照添加顺序渲染视图,后面的视图会覆盖前面的
- 对于复杂的叠加效果,注意渲染性能
- 考虑使用
offset修饰符来微调子视图位置
4.4 间距与对齐
间距设置
间距是布局中的重要因素,它决定了视图之间的关系和视觉舒适度。
// VStack 间距
VStack(spacing: 16) {
Text("项目 1")
Text("项目 2")
Text("项目 3")
}
// HStack 间距
HStack(spacing: 20) {
Text("左")
Text("中")
Text("右")
}
// 嵌套栈的间距
VStack(spacing: 20) {
Text("标题")
HStack(spacing: 10) {
Button("按钮 1") {}
Button("按钮 2") {}
}
Text("底部文本")
}
对齐设置
对齐决定了视图在容器中的位置,影响整体布局的一致性。
// 垂直对齐
VStack(alignment: .leading) {
Text("左对齐")
Text("这是一行更长的文本")
}
// 水平对齐
HStack(alignment: .center) {
Text("顶部")
.font(.largeTitle)
Text("底部")
.font(.footnote)
}
// 层叠对齐
ZStack(alignment: .bottom) {
Image(systemName: "photo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 200)
Text("图片标题")
.padding()
.background(Color.black.opacity(0.5))
.foregroundStyle(.white)
.frame(maxWidth: .infinity, alignment: .center)
}
内边距与外边距
内边距(padding)和外边距是控制视图与其他元素之间空间的重要工具。
// 内边距
VStack {
Text("带内边距的文本")
.padding() // 四周内边距
Text("自定义内边距")
.padding(.horizontal, 20) // 水平内边距
.padding(.vertical, 10) // 垂直内边距
}
// 外边距
VStack {
Text("带外边距的文本")
}
.padding() // 给整个 VStack 添加内边距
// 组合使用
Text("文本")
.padding(10) // 内边距
.background(Color.yellow)
.padding(10) // 外边距(看起来像内边距)
.background(Color.blue)
适用场景
- 表单设计:通过间距和对齐创建整齐的表单
- 卡片布局:使用内边距和外边距创建视觉层次感
- 响应式设计:根据不同屏幕尺寸调整间距
- 可访问性:适当的间距提高内容的可读性
最佳实践
- 保持一致的间距系统:使用统一的间距值(如 8、16、24 等)
- 考虑内容的重要性:重要内容之间应有更大的间距
- 响应式调整:在不同屏幕尺寸上调整间距
- 测试不同设备:确保在各种设备上布局都美观
4.5 垫片:Spacer
Spacer 介绍
Spacer 是 SwiftUI 中用于占据剩余空间的视图,它会自动扩展以填充可用空间。Spacer 是实现灵活布局的重要工具,特别适用于需要将元素推到容器边缘的场景。
基本用法
// 水平布局中的 Spacer
HStack {
Text("左侧")
Spacer() // 占据中间的所有空间
Text("右侧")
}
.padding()
// 垂直布局中的 Spacer
VStack {
Text("顶部")
Spacer() // 占据中间的所有空间
Text("底部")
}
.frame(height: 200)
.padding()
灵活使用
// 顶部对齐
VStack {
Text("标题")
Spacer()
}
.frame(height: 200)
.padding()
// 底部对齐
VStack {
Spacer()
Text("底部文本")
}
.frame(height: 200)
.padding()
// 两端对齐
HStack {
Text("左侧")
Spacer()
Text("中间")
Spacer()
Text("右侧")
}
.padding()
实际应用
// 工具栏布局
HStack {
Button("返回") {
print("返回")
}
Spacer()
Text("页面标题")
Spacer()
Button("更多") {
print("更多")
}
}
.padding()
.background(Color(.systemBackground))
// 表单底部按钮
VStack {
// 表单内容
ForEach(0..<3) {
Text("表单项 \($0 + 1)")
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
.padding(.horizontal)
}
Spacer()
// 底部按钮
Button("提交") {
print("提交")
}
.buttonStyle(.borderedProminent)
.padding()
}
适用场景
- 工具栏:将标题居中,按钮放在两侧
- 表单:将提交按钮固定在底部
- 卡片:将内容推到顶部,操作按钮放在底部
- 导航栏:创建平衡的布局
性能考虑
- Spacer 是轻量级视图,对性能影响很小
- 合理使用 Spacer 可以减少不必要的几何计算
- 避免在不需要的地方使用 Spacer,可能会导致意外的布局行为
4.6 布局修饰符
框架修饰符
frame 修饰符用于控制视图的大小和对齐方式。
// 设置固定大小
Text("固定大小")
.frame(width: 200, height: 100)
.background(Color.yellow)
// 设置最大和最小大小
Text("灵活大小")
.frame(minWidth: 100, maxWidth: 300, minHeight: 50, maxHeight: 150)
.background(Color.blue)
// 填充父容器
Text("填充")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.green)
// 带对齐的框架
Text("右对齐")
.frame(width: 200, alignment: .trailing)
.background(Color.gray.opacity(0.2))
位置修饰符
position 和 offset 修饰符用于调整视图的位置。
// 绝对位置
Text("绝对位置")
.position(x: 100, y: 100)
// 相对偏移
Text("相对偏移")
.offset(x: 50, y: 20)
// 组合使用
ZStack {
Text("基础位置")
.background(Color.yellow)
Text("偏移位置")
.offset(x: 50, y: 30)
.background(Color.red)
}
布局优先级
layoutPriority 修饰符用于设置视图的布局优先级。
HStack {
Text("短文本")
.layoutPriority(1) // 高优先级
.background(Color.yellow)
Text("这是一段非常长的文本,会被截断")
.background(Color.blue)
}
.frame(width: 200)
适用场景
- 响应式设计:根据屏幕尺寸调整视图大小
- 自定义布局:精确控制视图位置
- 复杂界面:处理不同优先级的内容
- 动态布局:根据内容自动调整
4.7 容器布局
List
List 是用于显示滚动列表的容器,自动处理单元格布局。
// 基本列表
List {
Text("项目 1")
Text("项目 2")
Text("项目 3")
}
// 带分组的列表
List {
Section(header: Text("分组 1")) {
Text("项目 1")
Text("项目 2")
}
Section(header: Text("分组 2")) {
Text("项目 3")
Text("项目 4")
}
}
ScrollView
ScrollView 用于创建可滚动的内容区域。
// 垂直滚动
ScrollView {
VStack(spacing: 20) {
ForEach(0..<20) {
Text("项目 \($0 + 1)")
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
}
}
.padding()
}
// 水平滚动
ScrollView(.horizontal) {
HStack(spacing: 20) {
ForEach(0..<10) {
Text("项目 \($0 + 1)")
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
}
}
.padding()
}
LazyVStack 和 LazyHStack
LazyVStack 和 LazyHStack 是延迟加载的栈容器,适用于大量数据。
// 延迟加载的垂直栈
ScrollView {
LazyVStack(spacing: 20) {
ForEach(0..<1000) {
Text("项目 \($0 + 1)")
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
}
}
.padding()
}
适用场景
- List:显示结构化数据列表
- ScrollView:显示超出屏幕的内容
- LazyVStack/LazyHStack:处理大量数据,提高性能
实战:创建一个登录页面
需求分析
创建一个包含以下元素的登录页面:
- 应用图标和标题
- 用户名输入框
- 密码输入框(带可见性切换)
- 登录按钮
- 忘记密码链接
- 注册链接
代码实现
import SwiftUI
struct LoginView: View {
// 状态变量
@State private var username = ""
@State private var password = ""
@State private var showPassword = false
var body: some View {
ZStack {
// 背景
LinearGradient(
colors: [.blue.opacity(0.1), .purple.opacity(0.1)],
startPoint: .top,
endPoint: .bottom
)
.ignoresSafeArea()
VStack(spacing: 24) {
// 应用图标和标题
VStack(spacing: 12) {
Image(systemName: "lock.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 80, height: 80)
.foregroundStyle(.blue)
Text("欢迎回来")
.font(.largeTitle)
.fontWeight(.bold)
Text("请登录以继续")
.foregroundStyle(.secondary)
}
// 输入区域
VStack(spacing: 16) {
// 用户名输入框
TextField(
"用户名",
text: $username,
prompt: Text("请输入用户名")
)
.textFieldStyle(.roundedBorder)
.padding(.horizontal)
// 密码输入框
ZStack(alignment: .trailing) {
if showPassword {
TextField(
"密码",
text: $password,
prompt: Text("请输入密码")
)
} else {
SecureField(
"密码",
text: $password,
prompt: Text("请输入密码")
)
}
Button(action: {
showPassword.toggle()
}) {
Image(systemName: showPassword ? "eye.slash.fill" : "eye.fill")
.foregroundStyle(.secondary)
.padding(.trailing, 16)
}
}
.textFieldStyle(.roundedBorder)
.padding(.horizontal)
// 忘记密码
HStack {
Spacer()
Button("忘记密码?") {
print("忘记密码")
}
.foregroundStyle(.blue)
.padding(.trailing)
}
}
// 登录按钮
Button("登录") {
print("登录")
}
.buttonStyle(.borderedProminent)
.tint(.blue)
.padding(.horizontal)
.frame(maxWidth: .infinity)
// 注册链接
HStack {
Text("还没有账号?")
Button("立即注册") {
print("注册")
}
.foregroundStyle(.blue)
}
Spacer()
}
.padding(.top, 60)
}
}
}
#Preview {
LoginView()
}
代码解析
- ZStack:用于层叠背景和内容,创建深度感
- VStack:用于垂直排列各个部分,保持页面结构清晰
- HStack:用于水平排列忘记密码链接和注册链接
- Spacer:用于底部填充空间,将内容推到顶部
- TextField 和 SecureField:用于用户输入
- Button:用于操作按钮
- LinearGradient:用于创建美观的背景渐变
- @State:用于管理视图状态
实战:创建一个产品详情页
需求分析
创建一个产品详情页,包含以下元素:
- 产品图片
- 产品标题和价格
- 产品描述
- 规格选择
- 购买按钮
代码实现
import SwiftUI
struct ProductDetailView: View {
// 状态变量
@State private var selectedColor = "红色"
@State private var selectedSize = "M"
@State private var quantity = 1
// 产品数据
let productName = "SwiftUI 高级教程"
let productPrice = "¥99.00"
let productDescription = "本教程涵盖了 SwiftUI 的高级特性,包括动画、手势、布局和性能优化等内容。通过实际项目案例,帮助你掌握 SwiftUI 的核心概念和最佳实践。"
let colors = ["红色", "蓝色", "黑色"]
let sizes = ["S", "M", "L", "XL"]
var body: some View {
ScrollView {
VStack(spacing: 20) {
// 产品图片
ZStack {
Color.gray.opacity(0.1)
.frame(height: 300)
Image(systemName: "book.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 150, height: 150)
.foregroundStyle(.blue)
}
// 产品信息
VStack(alignment: .leading, spacing: 12) {
HStack {
Text(productName)
.font(.title)
.fontWeight(.bold)
Spacer()
Text(productPrice)
.font(.title)
.fontWeight(.bold)
.foregroundStyle(.red)
}
// 产品描述
Text(productDescription)
.foregroundStyle(.secondary)
.lineLimit(nil)
// 颜色选择
Text("颜色")
.font(.headline)
HStack(spacing: 10) {
ForEach(colors, id: \.self) {
color in
Button(action: {
selectedColor = color
}) {
Text(color)
.padding(8)
.background(selectedColor == color ? Color.blue : Color.gray.opacity(0.1))
.foregroundStyle(selectedColor == color ? .white : .primary)
.cornerRadius(4)
}
}
}
// 尺寸选择
Text("尺寸")
.font(.headline)
HStack(spacing: 10) {
ForEach(sizes, id: \.self) {
size in
Button(action: {
selectedSize = size
}) {
Text(size)
.padding(8)
.background(selectedSize == size ? Color.blue : Color.gray.opacity(0.1))
.foregroundStyle(selectedSize == size ? .white : .primary)
.cornerRadius(4)
}
}
}
// 数量选择
Text("数量")
.font(.headline)
HStack {
Button(action: {
if quantity > 1 {
quantity -= 1
}
}) {
Image(systemName: "minus.circle")
.font(.system(size: 24))
}
Text("\(quantity)")
.font(.headline)
.padding(.horizontal, 20)
Button(action: {
quantity += 1
}) {
Image(systemName: "plus.circle")
.font(.system(size: 24))
}
}
}
.padding()
// 购买按钮
Button("加入购物车") {
print("加入购物车")
}
.buttonStyle(.borderedProminent)
.tint(.blue)
.padding(.horizontal)
.frame(maxWidth: .infinity)
.padding(.bottom, 30)
}
}
.navigationTitle("产品详情")
.navigationBarTitleDisplayMode(.inline)
}
}
#Preview {
ProductDetailView()
}
代码解析
- ScrollView:用于滚动显示产品详情
- ZStack:用于显示产品图片和背景
- VStack:用于垂直排列产品信息
- HStack:用于水平排列价格、颜色选择、尺寸选择和数量控制
- Button:用于选择颜色、尺寸和调整数量
- @State:用于管理用户选择的状态
小结
本章详细介绍了 SwiftUI 中的基础布局系统,包括:
- VStack:垂直堆叠视图,适用于从上到下的布局
- HStack:水平堆叠视图,适用于从左到右的布局
- ZStack:层叠视图,适用于需要层级关系的布局
- 间距与对齐:控制视图之间的空间和位置关系
- Spacer:占据剩余空间,实现灵活布局
- 布局修饰符:控制视图的大小、位置和优先级
- 容器布局:List、ScrollView、LazyVStack 等高级容器
- 实战案例:登录页面和产品详情页的完整实现
布局最佳实践
- 保持简洁:使用最少的容器实现所需布局
- 嵌套合理:避免过深的布局嵌套
- 响应式设计:考虑不同屏幕尺寸的布局适配
- 性能优化:对于大量数据使用 Lazy 容器
- 一致性:保持间距和对齐的一致性
- 可访问性:确保布局对所有用户都友好
通过本章的学习,你已经掌握了 SwiftUI 中最基本的布局技巧,能够创建各种常见的布局结构。在实际开发中,你可以根据具体需求选择合适的布局容器和技术,创建美观、响应式的用户界面。
参考资料
- SwiftUI 官方文档
- Apple Developer Documentation: VStack
- Apple Developer Documentation: HStack
- Apple Developer Documentation: ZStack
- Apple Developer Documentation: Spacer
- SwiftUI by Example
- SwiftUI Layout Essentials
本内容为《SwiftUI 基础教程》第四章,欢迎关注后续更新。