从基础到高级,涵盖各种View组件的使用。
SwiftUI 布局完全指南
项目结构
SwiftUILayoutTutorial/
├── App/
│ └── LayoutTutorialApp.swift
├── Views/
│ ├── ContentView.swift
│ ├── BasicViews/
│ │ ├── TextViews.swift
│ │ ├── ImageViews.swift
│ │ ├── ButtonViews.swift
│ │ └── InputViews.swift
│ ├── LayoutViews/
│ │ ├── StackLayouts.swift
│ │ ├── GridLayouts.swift
│ │ ├── ScrollViews.swift
│ │ └── ContainerViews.swift
│ ├── AdvancedViews/
│ │ ├── NavigationViews.swift
│ │ ├── TabViews.swift
│ │ ├── SheetAndAlert.swift
│ │ └── GestureViews.swift
│ └── Components/
│ ├── CustomModifiers.swift
│ ├── Animations.swift
│ └── Transitions.swift
- 主应用文件 (App/LayoutTutorialApp.swift)
swift
import SwiftUI
@main
struct LayoutTutorialApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
- 主内容视图 (Views/ContentView.swift)
swift
import SwiftUI
struct ContentView: View {
@State private var selectedTab = 0
var body: some View {
NavigationView {
List {
Section("基础视图组件") {
NavigationLink("文本视图 (Text)") { TextViews() }
NavigationLink("图片视图 (Image)") { ImageViews() }
NavigationLink("按钮视图 (Button)") { ButtonViews() }
NavigationLink("输入视图 (Input)") { InputViews() }
}
Section("布局容器") {
NavigationLink("堆栈布局 (HStack/VStack/ZStack)") { StackLayouts() }
NavigationLink("网格布局 (Grid)") { GridLayouts() }
NavigationLink("滚动视图 (ScrollView)") { ScrollViews() }
NavigationLink("容器视图 (Container)") { ContainerViews() }
}
Section("高级功能") {
NavigationLink("导航视图 (Navigation)") { NavigationViews() }
NavigationLink("标签视图 (TabView)") { TabViews() }
NavigationLink("弹窗和警告 (Sheet & Alert)") { SheetAndAlert() }
NavigationLink("手势识别 (Gesture)") { GestureViews() }
}
Section("动画与效果") {
NavigationLink("基础动画 (Animation)") { BasicAnimations() }
NavigationLink("转场效果 (Transition)") { TransitionViews() }
NavigationLink("自定义修饰符 (Modifier)") { CustomModifierViews() }
}
}
.navigationTitle("SwiftUI 布局教程")
.listStyle(InsetGroupedListStyle())
}
}
}
- 基础视图组件
3.1 文本视图 (Views/BasicViews/TextViews.swift)
swift
import SwiftUI
struct TextViews: View {
var body: some View {
ScrollView {
VStack(spacing: 30) {
// 基础文本
GroupBox("基础文本") {
VStack(spacing: 15) {
Text("普通文本")
Text("自定义字体大小")
.font(.title)
Text("加粗文本")
.fontWeight(.bold)
Text("斜体文本")
.italic()
Text("下划线文本")
.underline()
Text("删除线文本")
.strikethrough()
}
.frame(maxWidth: .infinity)
.padding()
}
// 文本颜色和背景
GroupBox("颜色和背景") {
VStack(spacing: 15) {
Text("红色文本")
.foregroundColor(.red)
Text("蓝色背景")
.background(Color.blue)
.foregroundColor(.white)
Text("圆角背景")
.padding()
.background(Color.green)
.cornerRadius(10)
Text("带边框的文本")
.padding()
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.blue, lineWidth: 2)
)
}
.frame(maxWidth: .infinity)
.padding()
}
// 文本样式
GroupBox("文本样式") {
VStack(spacing: 15) {
Text("多行文本会自动换行显示,当文本内容超过一行时,SwiftUI会自动处理换行")
.lineLimit(2)
Text("固定宽度的文本,超出部分会被截断...")
.frame(width: 200)
.truncationMode(.tail)
Text("自定义行间距")
.lineSpacing(10)
Text("字符间距")
.kerning(2)
Text("等宽字体")
.font(.system(.body, design: .monospaced))
}
.frame(maxWidth: .infinity)
.padding()
}
// 富文本
GroupBox("富文本") {
VStack(spacing: 15) {
Text("普通文本 + ") + Text("高亮文本")
.bold()
.foregroundColor(.blue) + Text(" + 普通文本")
Text("\(Image(systemName: "star.fill")) 带图标的文本")
}
.frame(maxWidth: .infinity)
.padding()
}
// 文本对齐
GroupBox("文本对齐") {
VStack(spacing: 15) {
Text("左对齐")
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color.gray.opacity(0.2))
Text("居中对齐")
.frame(maxWidth: .infinity, alignment: .center)
.background(Color.gray.opacity(0.2))
Text("右对齐")
.frame(maxWidth: .infinity, alignment: .trailing)
.background(Color.gray.opacity(0.2))
}
.frame(maxWidth: .infinity)
.padding()
}
}
.padding()
}
.navigationTitle("文本视图")
}
}
3.2 图片视图 (Views/BasicViews/ImageViews.swift)
swift
import SwiftUI
struct ImageViews: View {
var body: some View {
ScrollView {
VStack(spacing: 30) {
// 系统图标
GroupBox("SF Symbols 图标") {
HStack(spacing: 20) {
Image(systemName: "heart.fill")
.foregroundColor(.red)
.font(.largeTitle)
Image(systemName: "star.fill")
.foregroundColor(.yellow)
.font(.largeTitle)
Image(systemName: "person.circle.fill")
.foregroundColor(.blue)
.font(.largeTitle)
Image(systemName: "bell.fill")
.foregroundColor(.orange)
.font(.largeTitle)
}
.frame(maxWidth: .infinity)
.padding()
}
// 图片大小调整
GroupBox("图片大小调整") {
VStack(spacing: 15) {
Image(systemName: "photo")
.resizable()
.frame(width: 100, height: 100)
.foregroundColor(.blue)
Text("自适应比例")
.font(.caption)
Image(systemName: "photo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 100)
.foregroundColor(.green)
}
.frame(maxWidth: .infinity)
.padding()
}
// 图片样式
GroupBox("图片样式") {
VStack(spacing: 15) {
HStack(spacing: 20) {
Image(systemName: "photo")
.resizable()
.frame(width: 80, height: 80)
.clipShape(Circle())
Image(systemName: "photo")
.resizable()
.frame(width: 80, height: 80)
.clipShape(RoundedRectangle(cornerRadius: 15))
Image(systemName: "photo")
.resizable()
.frame(width: 80, height: 80)
.clipShape(Ellipse())
}
HStack(spacing: 20) {
Image(systemName: "photo")
.resizable()
.frame(width: 80, height: 80)
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.blue, lineWidth: 2)
)
Image(systemName: "photo")
.resizable()
.frame(width: 80, height: 80)
.shadow(radius: 5)
}
}
.frame(maxWidth: .infinity)
.padding()
}
// 图片效果
GroupBox("图片效果") {
VStack(spacing: 15) {
HStack(spacing: 20) {
Image(systemName: "photo")
.resizable()
.frame(width: 60, height: 60)
.opacity(0.5)
Image(systemName: "photo")
.resizable()
.frame(width: 60, height: 60)
.blur(radius: 2)
Image(systemName: "photo")
.resizable()
.frame(width: 60, height: 60)
.brightness(0.2)
}
HStack(spacing: 20) {
Image(systemName: "photo")
.resizable()
.frame(width: 60, height: 60)
.contrast(0.5)
Image(systemName: "photo")
.resizable()
.frame(width: 60, height: 60)
.saturation(0)
Image(systemName: "photo")
.resizable()
.frame(width: 60, height: 60)
.colorInvert()
}
}
.frame(maxWidth: .infinity)
.padding()
}
}
.padding()
}
.navigationTitle("图片视图")
}
}
3.3 按钮视图 (Views/BasicViews/ButtonViews.swift)
swift
import SwiftUI
struct ButtonViews: View {
@State private var buttonCount = 0
@State private var isPressed = false
var body: some View {
ScrollView {
VStack(spacing: 30) {
// 基础按钮
GroupBox("基础按钮") {
VStack(spacing: 15) {
Button("普通按钮") {
print("按钮被点击")
}
Button(action: {
buttonCount += 1
}) {
Text("计数按钮 (点击次数: \(buttonCount))")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
Button(action: {}) {
Label("带图标的按钮", systemImage: "star.fill")
.padding()
.background(Color.green)
.foregroundColor(.white)
.cornerRadius(10)
}
}
.frame(maxWidth: .infinity)
.padding()
}
// 按钮样式
GroupBox("按钮样式") {
VStack(spacing: 15) {
Button("边框按钮") {}
.buttonStyle(BorderedButtonStyle())
Button("边框突出按钮") {}
.buttonStyle(BorderedProminentButtonStyle())
Button("普通边框") {}
.buttonStyle(.bordered)
Button("突出边框") {}
.buttonStyle(.borderedProminent)
}
.frame(maxWidth: .infinity)
.padding()
}
// 自定义按钮
GroupBox("自定义按钮") {
VStack(spacing: 15) {
// 渐变按钮
Button("渐变按钮") {}
.padding(.horizontal, 30)
.padding(.vertical, 12)
.background(
LinearGradient(
gradient: Gradient(colors: [.blue, .purple]),
startPoint: .leading,
endPoint: .trailing
)
)
.foregroundColor(.white)
.cornerRadius(8)
// 阴影按钮
Button("带阴影的按钮") {}
.padding(.horizontal, 30)
.padding(.vertical, 12)
.background(Color.orange)
.foregroundColor(.white)
.cornerRadius(8)
.shadow(color: .orange.opacity(0.5), radius: 5, x: 0, y: 2)
// 圆形按钮
Button(action: {}) {
Image(systemName: "plus")
.font(.title)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.clipShape(Circle())
}
}
.frame(maxWidth: .infinity)
.padding()
}
// 按压效果按钮
GroupBox("按压效果") {
VStack(spacing: 15) {
Button("按压缩放效果") {
// 动作
}
.scaleEffect(isPressed ? 0.95 : 1.0)
.animation(.spring(), value: isPressed)
.onLongPressGesture(minimumDuration: 0) { pressing in
isPressed = pressing
} perform: {}
Button("涟漪效果") {}
.padding()
.background(Color.purple)
.foregroundColor(.white)
.cornerRadius(8)
.simultaneousGesture(
TapGesture()
.onEnded { _ in
// 添加涟漪效果
}
)
}
.frame(maxWidth: .infinity)
.padding()
}
}
.padding()
}
.navigationTitle("按钮视图")
}
}
3.4 输入视图 (Views/BasicViews/InputViews.swift)
swift
import SwiftUI
struct InputViews: View {
@State private var text = ""
@State private var secureText = ""
@State private var multiLineText = ""
@State private var sliderValue = 50.0
@State private var stepperValue = 0
@State private var toggleValue = false
@State private var pickerSelection = "选项1"
@State private var date = Date()
@State private var color = Color.blue
let pickerOptions = ["选项1", "选项2", "选项3"]
var body: some View {
ScrollView {
VStack(spacing: 25) {
// 文本输入
GroupBox("文本输入") {
VStack(spacing: 15) {
TextField("普通文本输入", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
SecureField("密码输入", text: $secureText)
.textFieldStyle(RoundedBorderTextFieldStyle())
TextEditor(text: $multiLineText)
.frame(height: 100)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color.gray.opacity(0.3), lineWidth: 1)
)
.cornerRadius(8)
if !text.isEmpty {
Text("输入的内容: \(text)")
.font(.caption)
}
}
.padding(.vertical, 8)
}
// 滑块和步进器
GroupBox("滑块和步进器") {
VStack(spacing: 15) {
HStack {
Text("滑块值: \(Int(sliderValue))")
Slider(value: $sliderValue, in: 0...100, step: 1)
}
HStack {
Text("步进值: \(stepperValue)")
Stepper("调整数值", value: $stepperValue, in: 0...10)
}
}
.padding(.vertical, 8)
}
// 开关和选择器
GroupBox("开关和选择器") {
VStack(spacing: 15) {
Toggle("开关按钮", isOn: $toggleValue)
if toggleValue {
Text("开关已打开")
.foregroundColor(.green)
}
Picker("选择器", selection: $pickerSelection) {
ForEach(pickerOptions, id: \.self) { option in
Text(option).tag(option)
}
}
.pickerStyle(SegmentedPickerStyle())
}
.padding(.vertical, 8)
}
// 日期选择器
GroupBox("日期选择器") {
VStack(spacing: 15) {
DatePicker("选择日期", selection: $date, displayedComponents: .date)
DatePicker("选择时间", selection: $date, displayedComponents: .hourAndMinute)
DatePicker("完整日期时间", selection: $date)
.datePickerStyle(CompactDatePickerStyle())
}
.padding(.vertical, 8)
}
// 颜色选择器
GroupBox("颜色选择器") {
ColorPicker("选择颜色", selection: $color)
.padding(.vertical, 8)
RoundedRectangle(cornerRadius: 10)
.fill(color)
.frame(height: 50)
}
// 表单输入组合
GroupBox("表单示例") {
VStack(spacing: 15) {
HStack {
Text("姓名:")
.frame(width: 60, alignment: .leading)
TextField("请输入姓名", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
HStack {
Text("年龄:")
.frame(width: 60, alignment: .leading)
Stepper("\(stepperValue)岁", value: $stepperValue, in: 0...120)
}
HStack {
Text("性别:")
.frame(width: 60, alignment: .leading)
Picker("", selection: $pickerSelection) {
Text("男").tag("男")
Text("女").tag("女")
}
.pickerStyle(SegmentedPickerStyle())
}
}
.padding(.vertical, 8)
}
}
.padding()
}
.navigationTitle("输入视图")
}
}
- 布局容器
4.1 堆栈布局 (Views/LayoutViews/StackLayouts.swift)
swift
import SwiftUI
struct StackLayouts: View {
var body: some View {
ScrollView {
VStack(spacing: 30) {
// HStack 水平布局
GroupBox("HStack - 水平布局") {
HStack(spacing: 20) {
ColorBlock(color: .red, text: "1")
ColorBlock(color: .green, text: "2")
ColorBlock(color: .blue, text: "3")
}
.frame(maxWidth: .infinity)
.padding()
Text("对齐方式")
.font(.caption)
HStack(alignment: .top, spacing: 20) {
ColorBlock(color: .orange, text: "顶部对齐", height: 80)
ColorBlock(color: .orange, text: "顶部对齐", height: 60)
ColorBlock(color: .orange, text: "顶部对齐", height: 100)
}
.frame(maxWidth: .infinity)
.padding()
.background(Color.gray.opacity(0.1))
}
// VStack 垂直布局
GroupBox("VStack - 垂直布局") {
VStack(spacing: 10) {
ColorBlock(color: .purple, text: "1")
ColorBlock(color: .purple, text: "2")
ColorBlock(color: .purple, text: "3")
}
.frame(maxWidth: .infinity)
.padding()
Text("对齐方式")
.font(.caption)
VStack(alignment: .leading, spacing: 10) {
Text("左对齐文本")
Text("这也是左对齐")
Text("更长的文本也是左对齐")
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(Color.gray.opacity(0.1))
}
// ZStack 层叠布局
GroupBox("ZStack - 层叠布局") {
ZStack {
ColorBlock(color: .blue.opacity(0.6), text: "底层")
ColorBlock(color: .green.opacity(0.6), text: "中间层")
ColorBlock(color: .yellow.opacity(0.6), text: "顶层")
}
.frame(height: 150)
.padding()
Text("层叠对齐示例")
.font(.caption)
ZStack(alignment: .bottomTrailing) {
RoundedRectangle(cornerRadius: 10)
.fill(Color.gray.opacity(0.2))
.frame(height: 150)
Text("右下角")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(5)
.padding()
}
.frame(maxWidth: .infinity)
.padding()
}
// 组合布局
GroupBox("组合布局示例") {
VStack(spacing: 15) {
HStack {
Image(systemName: "person.circle.fill")
.font(.largeTitle)
VStack(alignment: .leading) {
Text("张三")
.font(.headline)
Text("iOS开发者")
.font(.caption)
.foregroundColor(.gray)
}
Spacer()
Button("关注") {}
.buttonStyle(BorderedButtonStyle())
}
Divider()
HStack(spacing: 20) {
VStack {
Text("100")
.font(.title2)
.bold()
Text("文章")
.font(.caption)
}
VStack {
Text("1000")
.font(.title2)
.bold()
Text("粉丝")
.font(.caption)
}
VStack {
Text("500")
.font(.title2)
.bold()
Text("点赞")
.font(.caption)
}
}
.frame(maxWidth: .infinity)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
}
}
.padding()
}
.navigationTitle("堆栈布局")
}
}
// 辅助组件
struct ColorBlock: View {
let color: Color
let text: String
var height: CGFloat = 50
var body: some View {
RoundedRectangle(cornerRadius: 8)
.fill(color)
.frame(height: height)
.overlay(
Text(text)
.foregroundColor(.white)
.bold()
)
}
}
4.2 网格布局 (Views/LayoutViews/GridLayouts.swift)
swift
import SwiftUI
struct GridLayouts: View {
let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]
let adaptiveColumns = [
GridItem(.adaptive(minimum: 80))
]
let fixedColumns = [
GridItem(.fixed(100)),
GridItem(.fixed(100)),
GridItem(.fixed(100))
]
var body: some View {
ScrollView {
VStack(spacing: 30) {
// LazyVGrid 基础用法
GroupBox("LazyVGrid - 灵活网格") {
LazyVGrid(columns: columns, spacing: 15) {
ForEach(1...9, id: \.self) { index in
GridItemView(number: index, color: .blue)
}
}
.padding()
}
// 自适应网格
GroupBox("自适应网格") {
LazyVGrid(columns: adaptiveColumns, spacing: 15) {
ForEach(1...10, id: \.self) { index in
GridItemView(number: index, color: .green)
}
}
.padding()
}
// 固定宽度网格
GroupBox("固定宽度网格") {
LazyVGrid(columns: fixedColumns, spacing: 15) {
ForEach(1...6, id: \.self) { index in
GridItemView(number: index, color: .orange)
}
}
.padding()
}
// 不同大小的网格项
GroupBox("不同大小的网格项") {
let mixedColumns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]
LazyVGrid(columns: mixedColumns, spacing: 15) {
GridItemView(number: 1, color: .purple)
.frame(height: 100)
GridItemView(number: 2, color: .purple)
.frame(height: 60)
GridItemView(number: 3, color: .purple)
.frame(height: 80)
GridItemView(number: 4, color: .purple)
.frame(height: 120)
GridItemView(number: 5, color: .purple)
.frame(height: 70)
GridItemView(number: 6, color: .purple)
.frame(height: 90)
}
.padding()
}
// LazyHGrid 水平网格
GroupBox("LazyHGrid - 水平网格") {
ScrollView(.horizontal) {
LazyHGrid(rows: [GridItem(.fixed(100))], spacing: 15) {
ForEach(1...15, id: \.self) { index in
GridItemView(number: index, color: .red)
.frame(width: 100)
}
}
.padding()
}
}
// 实际应用示例 - 图片画廊
GroupBox("实际应用:图片画廊") {
LazyVGrid(columns: columns, spacing: 15) {
ForEach(1...6, id: \.self) { index in
VStack {
RoundedRectangle(cornerRadius: 10)
.fill(Color.blue.opacity(0.3))
.frame(height: 100)
.overlay(
Image(systemName: "photo")
.font(.largeTitle)
.foregroundColor(.blue)
)
Text("图片 \(index)")
.font(.caption)
}
}
}
.padding()
}
}
.padding()
}
.navigationTitle("网格布局")
}
}
struct GridItemView: View {
let number: Int
let color: Color
var body: some View {
RoundedRectangle(cornerRadius: 10)
.fill(color.opacity(0.2))
.frame(height: 80)
.overlay(
VStack {
Text("\(number)")
.font(.title2)
.bold()
.foregroundColor(color)
Text("项目")
.font(.caption)
.foregroundColor(color)
}
)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(color, lineWidth: 1)
)
}
}
4.3 滚动视图 (Views/LayoutViews/ScrollViews.swift)
swift
import SwiftUI
struct ScrollViews: View {
@State private var scrollOffset: CGFloat = 0
var body: some View {
VStack {
// 垂直滚动
GroupBox("垂直滚动") {
ScrollView {
VStack(spacing: 10) {
ForEach(1...20, id: \.self) { index in
HStack {
Text("项目 \(index)")
Spacer()
Image(systemName: "chevron.right")
.foregroundColor(.gray)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
}
}
.padding()
}
.frame(height: 300)
}
// 水平滚动
GroupBox("水平滚动") {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 15) {
ForEach(1...15, id: \.self) { index in
RoundedRectangle(cornerRadius: 10)
.fill(Color.blue.opacity(0.2))
.frame(width: 150, height: 120)
.overlay(
VStack {
Image(systemName: "photo")
.font(.largeTitle)
Text("图片 \(index)")
}
)
}
}
.padding()
}
.frame(height: 150)
}
// 带滚动位置的滚动视图
GroupBox("滚动位置追踪") {
ScrollView {
VStack(spacing: 10) {
ForEach(1...30, id: \.self) { index in
Text("滚动位置: \(Int(scrollOffset))")
.font(.caption)
.padding()
.background(Color.blue.opacity(0.1))
.cornerRadius(8)
.overlay(
GeometryReader { geometry in
Color.clear
.preference(key: ScrollOffsetKey.self,
value: geometry.frame(in: .global).minY)
}
)
}
}
.padding()
}
.frame(height: 200)
.onPreferenceChange(ScrollOffsetKey.self) { value in
scrollOffset = value
}
}
}
.padding()
.navigationTitle("滚动视图")
}
}
struct ScrollOffsetKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
- 高级功能
5.1 导航视图 (Views/AdvancedViews/NavigationViews.swift)
swift
import SwiftUI
struct NavigationViews: View {
var body: some View {
NavigationView {
List {
NavigationLink("基础导航") {
BasicNavigationDetail()
}
NavigationLink("带工具栏的导航") {
ToolbarNavigationDetail()
}
NavigationLink("多级导航") {
MultiLevelNavigation()
}
}
.navigationTitle("导航示例")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {}) {
Image(systemName: "plus")
}
}
}
}
}
}
struct BasicNavigationDetail: View {
var body: some View {
VStack(spacing: 20) {
Text("这是详细页面")
.font(.largeTitle)
NavigationLink("进入下一级") {
Text("第三级页面")
.navigationTitle("第三级")
}
}
.navigationTitle("详情")
.navigationBarTitleDisplayMode(.inline)
}
}
struct ToolbarNavigationDetail: View {
@State private var isEditing = false
var body: some View {
List(1...20, id: \.self) { item in
Text("项目 \(item)")
}
.navigationTitle("带工具栏")
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(action: { isEditing.toggle() }) {
Text(isEditing ? "完成" : "编辑")
}
Button(action: {}) {
Image(systemName: "trash")
}
}
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {}) {
Image(systemName: "folder")
}
}
ToolbarItem(placement: .bottomBar) {
HStack {
Text("共20个项目")
Spacer()
Button("添加") {}
}
}
}
}
}
struct MultiLevelNavigation: View {
var body: some View {
List {
NavigationLink("第一级") {
List {
NavigationLink("第二级") {
Text("第三级 - 最终页面")
.navigationTitle("第三级")
}
.navigationTitle("第二级")
}
.navigationTitle("第一级详情")
}
}
.navigationTitle("多级导航")
}
}
5.2 手势识别 (Views/AdvancedViews/GestureViews.swift)
swift
import SwiftUI
struct GestureViews: View {
@State private var tapCount = 0
@State private var longPressCount = 0
@State private var dragOffset = CGSize.zero
@State private var rotationAngle: Angle = .zero
@State private var scale: CGFloat = 1.0
@State private var swipeDirection = ""
var body: some View {
ScrollView {
VStack(spacing: 30) {
// 点击手势
GroupBox("点击手势") {
VStack {
Text("点击次数: \(tapCount)")
.padding()
.background(Color.blue.opacity(0.2))
.cornerRadius(10)
.onTapGesture {
tapCount += 1
}
Text("双击手势")
.padding()
.background(Color.green.opacity(0.2))
.cornerRadius(10)
.onTapGesture(count: 2) {
tapCount += 2
}
}
.frame(maxWidth: .infinity)
.padding()
}
// 长按手势
GroupBox("长按手势") {
Text("长按次数: \(longPressCount)")
.padding()
.background(Color.orange.opacity(0.2))
.cornerRadius(10)
.onLongPressGesture(minimumDuration: 1) {
longPressCount += 1
}
.frame(maxWidth: .infinity)
.padding()
}
// 拖拽手势
GroupBox("拖拽手势") {
Circle()
.fill(Color.blue)
.frame(width: 80, height: 80)
.offset(dragOffset)
.gesture(
DragGesture()
.onChanged { gesture in
dragOffset = gesture.translation
}
.onEnded { _ in
withAnimation(.spring()) {
dragOffset = .zero
}
}
)
.frame(maxWidth: .infinity)
.padding()
Text("拖拽圆点试试")
.font(.caption)
}
// 旋转和缩放手势
GroupBox("旋转和缩放手势") {
RoundedRectangle(cornerRadius: 20)
.fill(Color.purple.opacity(0.3))
.frame(width: 150, height: 150)
.rotationEffect(rotationAngle)
.scaleEffect(scale)
.gesture(
RotationGesture()
.onChanged { angle in
rotationAngle = angle
}
.simultaneously(with:
MagnificationGesture()
.onChanged { value in
scale = value
}
)
)
.onTapGesture(count: 2) {
withAnimation(.spring()) {
rotationAngle = .zero
scale = 1.0
}
}
.frame(maxWidth: .infinity)
.padding()
Text("双指旋转或缩放,双击重置")
.font(.caption)
}
// 滑动手势
GroupBox("滑动手势") {
Text("滑动方向: \(swipeDirection)")
.padding()
.frame(maxWidth: .infinity)
.background(Color.yellow.opacity(0.2))
.cornerRadius(10)
.gesture(
DragGesture(minimumDistance: 50)
.onEnded { gesture in
let horizontal = gesture.translation.width
let vertical = gesture.translation.height
if abs(horizontal) > abs(vertical) {
swipeDirection = horizontal > 0 ? "向右滑动" : "向左滑动"
} else {
swipeDirection = vertical > 0 ? "向下滑动" : "向上滑动"
}
}
)
.padding()
}
// 组合手势
GroupBox("组合手势") {
Rectangle()
.fill(Color.red.opacity(0.3))
.frame(height: 150)
.overlay(
VStack {
Image(systemName: "hand.tap")
.font(.largeTitle)
Text("点击或拖拽")
.font(.caption)
}
)
.gesture(
TapGesture()
.onEnded { _ in
print("点击了")
}
.exclusively(before:
DragGesture()
.onEnded { _ in
print("拖拽了")
}
)
)
.padding()
}
}
.padding()
}
.navigationTitle("手势识别")
}
}
这个教程涵盖了SwiftUI的主要布局功能,从基础的文本、图片、按钮,到复杂的布局容器和高级手势。每个部分都有详细的代码示例和实际应用场景。