SwiftUI语法-5分钟复习
- 2026-10-10 04:49:02
1.SwiftUI 基本结构
SwiftUI 使用 View 描述界面:
import SwiftUI
structContentView: View {
var body: someView {
Text("Hello SwiftUI")
}
}核心:
View
↓
body
↓
some View
↓
声明式描述 UI2. App 入口
SwiftUI App 通常从 @main 开始:
@main
structMyApp: App {
var body: someScene {
WindowGroup {
ContentView()
}
}
}结构:
App
↓
Scene
↓
WindowGroup
↓
ContentView3. Text
显示文字:
Text("Hello SwiftUI")
.font(.title)
.fontWeight(.bold)
.foregroundStyle(.blue)字符串插值:
let name ="Tom"
Text("Hello \(name)")4. Image
系统图标:
Image(systemName: "heart.fill")
.font(.title)
.foregroundStyle(.red)资源图片:
Image("avatar")
.resizable()
.scaledToFit()
.frame(width: 100, height: 100)5. Button
按钮:
Button("登录") {
print("login")
}自定义内容:
Button {
print("login")
} label: {
Label("登录", systemImage: "person")
}6. VStack
垂直布局:
VStack(spacing: 10) {
Text("A")
Text("B")
Text("C")
}效果:
A
B
C7. HStack
水平布局:
HStack {
Text("A")
Text("B")
Text("C")
}效果:
A B C8. ZStack
层叠布局:
ZStack {
Color.blue
Text("Hello")
.foregroundStyle(.white)
}类似:
背景
↑
前景9. Spacer
占据剩余空间:
HStack {
Text("左边")
Spacer()
Text("右边")
}常用于:
左边 右边10. Divider
分割线:
VStack {
Text("第一行")
Divider()
Text("第二行")
}11. Modifier
SwiftUI 大量使用 Modifier:
Text("Hello")
.font(.title)
.foregroundStyle(.blue)
.padding()
.background(.gray)可以理解为:
View
↓ modifier
View
↓ modifier
ViewModifier 顺序可能影响最终效果。SwiftUI Modifier 不是“给原 View 设置一堆属性”,而是前一个 Modifier 产生新 View,后一个 Modifier 再修饰这个新 View。
padding
frame
background
overlay
clipShape
border
offset
scaleEffect
rotationEffect
opacity
gesture
contentShape需要特别注意这些涉及尺寸、布局、绘制范围、裁剪、变换、点击区域的 Modifier。
12. frame
设置 View 的布局尺寸:
Text("Hello").frame(width: 200, height: 100)占满可用宽度:
Text("Hello").frame(maxWidth: .infinity)13. padding
设置内边距:
Text("Hello") .padding()指定方向:
Text("Hello")
.padding(.horizontal, 20)
.padding(.vertical, 10)14. background
设置背景:
Text("Hello")
.padding()
.background(.blue)配合圆角:
Text("Hello")
.padding()
.background(.blue)
.clipShape(RoundedRectangle(cornerRadius: 10))15. @State
View 自己管理的状态:
structCounterView: View {
@Stateprivatevar count =0
var body: someView {
Button("Count: \(count)") {
count +=1
}
}
}核心:
State 改变
↓
SwiftUI 检测变化
↓
重新计算 body
↓
更新需要变化的 UI16. @Binding
父组件把状态的“读写能力”传给子组件:
structParentView: View {
@Stateprivatevar name =""
var body: someView {
ChildView(name: $name)
}
}
structChildView: View {
@Bindingvar name: String
var body: someView {
TextField("姓名", text: $name)
}
}理解:
Parent
@State name
↓ $name
Child
@Binding nameParentView传值到ChildView时使用的是$name,有就是传递的是Binding<String>.
17. @Observable
现代 SwiftUI 常使用 Observation 管理模型状态:
@Observable
classUserModel {
var name ="Tom"
var age =20
}使用:
structContentView: View {
@Stateprivatevar model =UserModel()
var body: someView {
Text(model.name)
}
}model.name 改变后,依赖它的 UI 可以自动更新。
18. @StateObject
传统 ObservableObject 模式中,如果 View 创建并持有对象:
classUserViewModel: ObservableObject {
@Publishedvar name ="Tom"
}
structContentView: View {
@StateObjectprivatevar vm =UserViewModel()
var body: someView {
Text(vm.name)
}
}关系:
ObservableObject
+
@Published
↓
@StateObject
↓
View@StateObject用来让 SwiftUI View 创建、持有并观察一个ObservableObject对象。
传统的 ObservableObject + @Published 状态管理体系。建议使用@Observable。
19. @ObservedObject
对象由外部创建,当前 View 只是观察:
structUserView: View {
@ObservedObjectvar vm: UserViewModel
var body: someView {
Text(vm.name)
}
}区别:
@StateObject
→ 当前 View 创建/持有
@ObservedObject
→ 外部传入,当前 View 观察建议使用@Observable。
传统方案
ObservableObject
+
@Published
+
@StateObject/@ObservedObject
现代方案
@Observable
+
普通属性
+
@State/@Environment 等20. @Environment
读取 SwiftUI 环境中的值:
@Environment(\.dismiss)
privatevar dismiss从当前 View 的 SwiftUI Environment 中取出 dismiss 这个能力,赋给 dismiss 属性。使用:
Button("关闭") {
dismiss()
}还可以:
@Environment(\.colorScheme)
privatevar colorScheme@Environment用来从 SwiftUI 当前 View 所处的“环境”中读取一个值。
最常用的 @Environment
| 属性 | 类型/概念 | 作用 |
dismiss | DismissAction | |
colorScheme | ColorScheme | |
locale | Locale | |
calendar | Calendar | |
timeZone | TimeZone | |
scenePhase | ScenePhase | |
horizontalSizeClass | UserInterfaceSizeClass? | |
verticalSizeClass | UserInterfaceSizeClass? | |
dynamicTypeSize | DynamicTypeSize | |
layoutDirection | LayoutDirection | |
displayScale | CGFloat | |
isEnabled | Bool | |
isPresented | Bool | |
isSearching | Bool | |
editMode | Binding<EditMode>? | |
openURL | OpenURLAction | |
refresh | RefreshAction? | |
undoManager | UndoManager? | |
modelContext | ModelContext | |
managedObjectContext | NSManagedObjectContext | |
accessibilityReduceMotion | Bool | |
accessibilityVoiceOverEnabled | Bool | |
accessibilityDifferentiateWithoutColor | Bool |
21. TextField
输入框:
@Stateprivatevar name =""
var body: someView {
TextField("请输入姓名", text: $name)
}通常绑定:
TextField
↕
Binding
↕
State22. Toggle
开关:
@Stateprivatevar enabled =false
var body: someView {
Toggle("开启通知", isOn: $enabled)
}Toggle选中后enabled值变化。
23. Picker
选择器:
@Stateprivatevar index =0
var body: someView {
Picker("选择", selection: $index) {
Text("Apple").tag(0)
Text("Google").tag(1)
}
}selection当前选中的值,用户选择后index的值也会直接变化。
24. ScrollView
滚动容器, 像UIScrollView:
ScrollView {
VStack {
ForEach(0..<100) { index in
Text("Item \(index)")
}
}
}横向:
ScrollView(.horizontal) {
HStack {
// ...
}
}25. List
列表,像UITableView:
List {
Text("Apple")
Text("Google")
Text("Microsoft")
}动态数据:
let names = ["Apple", "Google", "Microsoft"]
List(names, id: \.self) { name in
Text(name)
}提供的详细功能,见文尾的补充。
26. ForEach
根据数据创建 View:
VStack {
ForEach(0..<5) { index in
Text("第 \(index) 行")
}
}模型通常实现:
structUser: Identifiable {
let id: UUID
let name: String
}然后:
ForEach(users) { user in
Text(user.name)
}27. LazyVStack / LazyHStack
大量数据时延迟创建:
ScrollView {
LazyVStack {
ForEach(0..<1000) { index in
Text("Item \(index)")
}
}
}对应关系:
VStack 普通垂直布局
LazyVStack 延迟创建垂直内容
HStack 普通水平布局
LazyHStack 延迟创建水平内容28. NavigationStack
现代 SwiftUI 导航:
NavigationStack {
NavigationLink("详情") {
DetailView()
}
.navigationTitle("首页")
}基本结构:
NavigationStack
↓
Home
↓
NavigationLink
↓
Detail可以设置多个链接:
NavigationStack {
List {
NavigationLink {
ContentView()
} label: {
Label("个人资料", systemImage: "person")
}
NavigationLink {
ContentView()
} label: {
Label("设置", systemImage: "gear")
}
}
.navigationTitle("我的")
}29. NavigationPath
代码控制导航栈:
@Stateprivatevar path =NavigationPath()
var body: someView {
NavigationStack(path: $path) {
Button("详情") {
path.append(100)
}
.navigationDestination(for: Int.self) { userId in
UserDetailView(userId: userId)
}
}
}NavigationPath就是 SwiftUI 中用数据描述导航栈,append()相当于 Push,removeLast()相当于 Pop,navigationDestination负责定义“某种数据应该显示哪个页面”。
将$path传到子页面,在子页面可以继续append()到其他子页面。
30. TabView
底部 Tab:
TabView {
HomeView()
.tabItem {
Label("首页", systemImage: "house")
}
ProfileView()
.tabItem {
Label("我的", systemImage: "person")
}
}31. sheet
弹出页面:
@Stateprivatevar showDetail =false
var body: someView {
Button("打开") {
showDetail =true
}
.sheet(isPresented: $showDetail) {
DetailView()
}
}当showDetail为true的时候展示sheet。通过Modifier提供:
.padding()
.background()
.frame()
.sheet()
.alert()
.navigationTitle()也就是SwiftUI View都可以挂这些Modfier。
32. alert
弹出 Alert:
@Stateprivatevar showAlert =false
var body: someView {
Button("删除") {
showAlert =true
}
.alert("确认删除?", isPresented: $showAlert) {
Button("删除", role: .destructive) {
delete()
}
Button("取消", role: .cancel) {}
}
}33. task
View 出现后执行异步任务:
structUserView: View {
@Stateprivatevar name =""
var body: someView {
Text(name)
.task {
name =await loadUser()
}
}
}常用于:
页面出现
↓
.task
↓
调用 async API
↓
修改 State
↓
刷新 UI在task中调用ViewModel中的方法进行数据的获取。task与View的生命周期关联,当View消失时,会主动取消task的请求。
34. onAppear / onDisappear
监听 View 出现和消失:
Text("Hello")
.onAppear {
print("appear")
}
.onDisappear {
print("disappear")
}35. onChange
监听状态变化:
@Stateprivatevar name =""
var body: someView {
TextField("姓名", text: $name)
.onChange(of: name) {
print("name changed")
}
}当name发生变化的时候,onChange被调用。
36. animation
状态变化时执行动画:
@Stateprivatevar expanded =false
var body: someView {
Rectangle()
.frame(
width: expanded ?200 : 100,
height: 100
)
.animation(.easeInOut, value: expanded)
.onTapGesture {
expanded.toggle()
}
}也可以:
@Stateprivatevar expanded =false
var body: someView {
VStack {
Rectangle()
.fill(.blue)
.frame(
width: expanded ?200 : 100,
height: 100
)
Button("切换大小") {
withAnimation {
expanded.toggle()
}
}
}
}37. Gesture
点击:
Text("点击")
.onTapGesture {
print("tap")
}长按:
Text("长按")
.onLongPressGesture {
print("long press")
}38. GeometryReader
获取父布局提供的尺寸:
GeometryReader { geometry in
Text("宽度:\(geometry.size.width)")
.frame(width: geometry.size.width)
}适合需要根据容器尺寸计算布局的场景,但不应把它当成所有布局问题的默认方案。
39. safeArea
忽略安全区域:
Color.blue
.ignoresSafeArea()常用于全屏背景。
40. ViewBuilder
允许一个闭包声明多个 View:
@ViewBuilder
funcheader() -> someView {
Text("标题")
Text("副标题")
}自定义容器:
structCard<Content: View>: View {
@ViewBuilder
let content: () -> Content
var body: someView {
VStack {
content()
}
.padding()
}
}使用:
Card {
Text("Hello SwiftUI")
Text("Hello World")
}ViewBuilder接受一个闭包,里面可以放置多个View.
41. some View
SwiftUI 的 body:
var body: someView {
Text("Hello")
}some 是一个关键字,表示 不透明返回类型(opaque return type), some View表示:
返回一个确定的 View 类型。
但调用者不需要知道具体类型some View表示确定的View类型。
使用any View表示不同的View类型。
42. 条件 UI
根据状态显示不同 View:
if isLogin {
HomeView()
} else {
LoginView()
}也可以:
if loading {
ProgressView()
} else {
ContentView()
}43. ProgressView
加载状态:
ProgressView("加载中...")进度:
structContentView: View {
@Stateprivatevar progress: Double=1.0
var body: someView {
VStack {
ProgressView("加载中")
ProgressView(value: progress, total: 100.0)
Button("点我") {
progress +=1.0
}
}
}
}44. 自定义 View
SwiftUI 推荐拆分小组件:
structUserCard: View {
let name: String
var body: someView {
HStack {
Image(systemName: "person.circle")
Text(name)
}
.padding()
}
}使用:
UserCard(name: "Tom")45. ViewModifier
复用一组 Modifier:
structCardStyle: ViewModifier {
funcbody(content: Content) -> someView {
content
.padding()
.background(.white)
.clipShape(RoundedRectangle(cornerRadius: 12))
.shadow(radius: 3)
}
}使用:
Text("Hello")
.modifier(CardStyle())ViewModifier作用是:把一组 View 样式或行为封装起来,方便在不同的 View 上重复使用。
46. Preview
预览 View:
#Preview {
ContentView()
}可以快速查看组件效果,不需要每次运行整个 App。
47. SwiftUI + MVVM
常见项目结构:
View
↓ 用户操作
ViewModel
↓
Repository / Service
↓
Network / Database例如:
@Observable
classUserViewModel {
var users: [User] = []
funcload() async {
users =await repository.loadUsers()
}
}View:
structUserView: View {
@Stateprivatevar vm =UserViewModel()
var body: someView {
List(vm.users) { user in
Text(user.name)
}
.task {
await vm.load()
}
}
}做到“各司其职”。
48. SwiftUI 最重要的数据流
SwiftUI 最核心的思想不是控件,而是:
State
↓
View = f(State)
↓
用户操作
↓
修改 State
↓
重新计算 View例如:
@Stateprivatevar count =0
Button("Count \(count)") {
count +=1
}不是:
找到 UILabel
↓
修改 UILabel.text而是:
修改数据
↓
SwiftUI 根据数据重新描述 UI49. UIKit 与 SwiftUI 思维对比
UIKit
UIViewController
↓
创建 UIView
↓
设置约束
↓
找到控件
↓
手动修改 UI
SwiftUI
State
↓
body
↓
声明 UI
↓
State 改变
↓
SwiftUI 自动更新 UI核心转变:
UIKit:命令式 UI
SwiftUI:声明式 UI补充
List提供的功能
1. Row:列表中的一行
List 里面的每一个元素通常就是一个 Row。
List {
Text("张三")
Text("李四")
Text("王五")
}效果:
┌──────────────┐
│ 张三 │ ← Row
├──────────────┤
│ 李四 │ ← Row
├──────────────┤
│ 王五 │ ← Row
└──────────────┘Row 可以是复杂 View:
List(users) { user in
HStack {
Image(systemName: "person.circle")
VStack(alignment: .leading) {
Text(user.name)
Text(user.phone)
.font(.caption)
}
}
}2. Section:列表分组
可以把多个 Row 分成不同组:
List {
Section("账号") {
Text("个人资料")
Text("修改密码")
}
Section("系统") {
Text("通知")
Text("隐私")
}
}效果类似 iPhone 设置:
账号
┌──────────────┐
│ 个人资料 │
│ 修改密码 │
└──────────────┘
系统
┌──────────────┐
│ 通知 │
│ 隐私 │
└──────────────┘3. Header / Footer:分组头和分组尾
Section 可以有 Header 和 Footer。
List {
Section {
Text("Wi-Fi")
Text("蓝牙")
} header: {
Text("网络")
} footer: {
Text("修改网络相关设置")
}
}结构:
网络 ← Header
┌─────────────────┐
│ Wi-Fi │
│ 蓝牙 │
└─────────────────┘
修改网络相关设置 ← FooterHeader 通常是标题,Footer 通常是补充说明。
4. 选择 Selection:选中某一行
例如:
@Stateprivatevar selection: String?
var body: someView {
List(selection: $selection) {
Text("Apple")
.tag("Apple")
Text("Google")
.tag("Google")
Text("Microsoft")
.tag("Microsoft")
}
}当用户选择:
Apple那么:
selection =="Apple"它在 iPad/macOS 的侧边栏、主从布局等场景尤其常见。
5. 删除 onDelete
List + ForEach 可以直接提供系统列表删除行为:
@Stateprivatevar users = [
"张三",
"李四",
"王五"
]
var body: someView {
List {
ForEach(users, id: \.self) { user in
Text(user)
}
.onDelete { indexSet in
users.remove(atOffsets: indexSet)
}
}
}用户可以通过系统提供的列表交互删除 Row。
核心:
用户删除某行
↓
onDelete
↓
得到 IndexSet
↓
删除数据
↓
List 更新6. 移动 onMove
允许用户调整 Row 顺序:
List {
ForEach(users, id: \.self) { user in
Text(user)
}
.onMove { source, destination in
users.move(
fromOffsets: source,
toOffset: destination
)
}
}比如:
原来
1 张三
2 李四
3 王五
用户拖动
↓
1 王五
2 张三
3 李四常用于:
频道排序
收藏排序
播放列表排序
功能入口排序7. Swipe Actions:左滑/右滑操作
这是非常常见的功能。
List(users) { user in
Text(user.name)
.swipeActions {
Button("删除", role: .destructive) {
delete(user)
}
}
}用户滑动一行:
┌──────────────────────────┐
│ 张三 │ 删除 │
└──────────────────────────┘也可以多个按钮:
.swipeActions {
Button("删除", role: .destructive) {
delete(user)
}
Button("收藏") {
favorite(user)
}
}类似:
邮件 App
消息 App里面常见的左滑删除、标记等操作。
8. 列表样式 listStyle
List 自带不同的系统列表样式。
例如:
List {
Section("账号") {
Text("个人资料")
Text("修改密码")
}
}
.listStyle(.insetGrouped)常见:
.listStyle(.plain)
.listStyle(.inset)
.listStyle(.grouped)
.listStyle(.insetGrouped)你可以粗略理解:
plain
→ 普通连续列表
grouped
→ 分组列表
insetGrouped
→ 类似 iPhone 设置 App 的圆角分组效果具体视觉表现会根据 iOS/macOS 和系统版本有所变化。
9. 编辑模式 EditMode
List 可以进入编辑状态。
最经典:
List {
ForEach(users, id: \.self) { user in
Text(user)
}
.onDelete { indexSet in
users.remove(atOffsets: indexSet)
}
.onMove { source, destination in
users.move(
fromOffsets: source,
toOffset: destination
)
}
}
.toolbar {
EditButton()
}点击:
编辑
↓
List 进入 EditMode
↓
可以删除 / 拖动排序
↓
完成如果需要自己读取编辑状态:
@Environment(\.editMode)
privatevar editModedo-try-catch的使用
do {
let user =try loadUser()
print(user.name)
} catch {
print("加载失败:\(error)")
}注意三个关键字:
| 关键字 | 作用 |
do | |
try | |
catch |
比Java/ Kotlin / JavaScript语言多了一个do关键字。而try的作用也有区别:
1.try:正常的错误处理
do {
let result =try login(
username: "admin",
password: "123456"
)
print(result)
} catch {
print(error)
}特点:
• 成功:返回正常结果。 • 失败:抛出错误。 • 错误可以由 catch捕获,也可以继续向上抛出。
这是最常用、最标准的写法。
2.try?:将错误转换为 nil
let result =try? login(
username: "admin",
password: "wrong"
)如果登录失败:result == nil
如果成功:result == Optional("登录成功")
不关心具体错误原因,只关心成功还是失败。
3.try!:确信不会出错
let result =try! login(
username: "admin",
password: "123456"
)如果成功:返回正常结果
如果抛出错误:程序运行时触发致命错误
实际开发中尽量避免try!,除非能够确保不会抛出错误。
4. 单独使用try
funcloadData() throws -> String {
let name =try loadUser()
return name
}5. try await一起使用
funcloadUser() asyncthrows -> String {
tryawaitTask.sleep(for: .seconds(1))
return"张三"
}| 关键字 | 作用 |
async | |
throws | |
try | |
await |
结尾
接连着学习了Flutter,Jetpack Compose,Dart语法,Swift语法,React Native和现在的SwiftUI后,感觉到了千变一律啊,那种返璞归真的错觉。似乎再发展几年,所有的语言和UI相关,都可以回归到同一套了。大家功能相似,作用一样,搞那么多没用啊。
AI又来替代手搓代码,那代码的差异性进一步缩小了。AI眼中所有语言都是一样的吧。很期待5年或10年后,程序员行业会变为怎样。希望我还是站在风里,跟着一直成长!!!