SwiftUI布局完全指南:从入门到精通

从基础到高级,涵盖各种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
  1. 主应用文件 (App/LayoutTutorialApp.swift)
swift 复制代码
import SwiftUI

@main
struct LayoutTutorialApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
  1. 主内容视图 (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())
        }
    }
}
  1. 基础视图组件

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("输入视图")
    }
}
  1. 布局容器

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()
    }
}
  1. 高级功能

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的主要布局功能,从基础的文本、图片、按钮,到复杂的布局容器和高级手势。每个部分都有详细的代码示例和实际应用场景。

相关推荐
李游Leo10 分钟前
HarmonyOS 7 实战开发 05:把页面做到可上线状态
ios·harmonyos
终端安全笔记6 小时前
描述文件、企业证书、MDM 不是三个名字,是三层
android·ios·智能手机
传奇开心果编程6 小时前
【SwiftUI入门练中学】第1课 从零开始
学习·ui·ios·swiftui·swift
丿Wayne12 小时前
iOS Token 刷新实战(Actor 避免并发重复刷新)
ios·swift·token·actor·swift并发
终端安全笔记1 天前
iOS 27 给了租赁一个新工具,但它只认受监督的设备
android·网络·安全·ios
大熊猫侯佩1 天前
独立 App 首发完成 iPhone Duo 展开态适配
ios·swiftui·swift
光影少年1 天前
setImmediate 和 setTimeout(0) 的区别
android·前端·react.js·ios·前端框架
2501_916008891 天前
SSE 和 gRPC 流式接口如何抓包,调试 SSE 与 gRPC 流式接口
网络协议·计算机网络·网络安全·ios·adb·https·udp
HouWan2 天前
Flutter: MediaQuery.of(context) 为什么可能拖慢页面?
android·flutter·ios
SoaringHeart2 天前
Swift 进阶&封装:仿 Get 给SwiftUI 添加路由导航能力
swiftui·swift