SwiftUI 传值方式系统整理
在 SwiftUI 中,所谓“传值”本质上是 状态(State)和数据流(Data Flow)在不同 View 之间的传播方式。fullScreenCover、sheet、NavigationStack 只是视图呈现手段,本身并不负责数据传递。
本文从基础到工程化,系统整理 SwiftUI 中常见、可落地的传值方式。
一、初始化传值(父 → 子,最推荐)
1. 构造函数参数(Value Injection)
适用场景:
- 父 View → 子 View
- 一次性数据
- 只读或本地修改
struct DetailView: View {
let id: Int
}
DetailView(id: 10)
用于 fullScreenCover:
.fullScreenCover(isPresented: $showDetail) {
DetailView(id: selectedId)
}
特点:
- 类型安全
- 结构清晰
- SwiftUI 首选方式
二、状态绑定传值(父 ↔ 子)
2. @Binding
适用场景:
- 编辑页
- 表单
- 子 View 需要修改父状态
struct EditView: View {
@Binding var text: String
}
父 View:
@State private var name = ""
.fullScreenCover(isPresented: $showEdit) {
EditView(text: $name)
}
特点:
- 双向同步
- 生命周期由父 View 控制
sheet/fullScreenCover通用
三、ViewModel 传值(工程化首选)
3. ObservableObject 注入
ViewModel
final class UserViewModel: ObservableObject {
@Published var name: String
init(name: String) {
self.name = name
}
}
方式一:新建 VM
.fullScreenCover(isPresented: $show) {
UserView(vm: UserViewModel(name: "Tom"))
}
方式二:共享 VM
@StateObject var vm = UserViewModel(name: "Tom")
.fullScreenCover(isPresented: $show) {
UserView(vm: vm)
}
特点:
- 清晰的状态归属
- 适合复杂页面
- MVVM 标准做法
四、全局传值(跨层级共享)
4. @EnvironmentObject
全局状态
final class AppState: ObservableObject {
@Published var isLogin = false
}
注入:
RootView()
.environmentObject(AppState())
使用:
@EnvironmentObject var appState: AppState
用于 fullScreenCover:
.fullScreenCover(isPresented: $showLogin) {
LoginView()
.environmentObject(appState)
}
适用场景:
- 登录态
- 用户信息
- 主题 / 权限 / 全局配置
五、导航栈传值(iOS 16+)
5. NavigationStack + navigationDestination
NavigationStack {
NavigationLink(value: user) {
Text(user.name)
}
}
.navigationDestination(for: User.self) { user in
UserDetailView(user: user)
}
特点:
- 路由状态化
- 支持深链与状态恢复
- 非常适合列表 → 详情
六、回调闭包(子 → 父,行为型)
6. Closure 回传
struct EditView: View {
let onSave: (String) -> Void
}
.fullScreenCover(isPresented: $showEdit) {
EditView { newName in
name = newName
}
}
特点:
- 明确表达“事件”而非状态
- 不暴露父 View 内部结构
- 非常适合工程化设计
七、Dismiss + 共享状态
7. @Environment(.dismiss)
@Environment(.dismiss) var dismiss
常与以下方式配合:
@BindingObservableObject
使用模式:
- 子 View 修改状态
- 调用
dismiss()关闭页面 - 不直接 return 数据
八、PreferenceKey(高级,不建议常规使用)
8. PreferenceKey
用途:
- 子 View → 父 View
- 布局信息回传
典型场景:
- 自定义导航栏高度
- 复杂布局反馈
不适合:
- 普通业务数据传递
九、fullScreenCover 专属注意点
1. 控制权来自状态
.fullScreenCover(isPresented: $isPresented) { ... }
2. View 会被多次创建
- 不要在
body中初始化重要状态 - 使用
@StateObject持有 ViewModel
3. 推荐 item:(iOS 16+)
.fullScreenCover(item: $selectedUser) { user in
UserView(user: user)
}
优势:
- 生命周期与数据强绑定
- 减少状态不同步问题
十、传值方式速查表
| 场景 | 推荐方式 |
|---|---|
| 父 → 子(一次性) | init 参数 |
| 父 ↔ 子(编辑) | @Binding |
| 复杂页面 | ObservableObject |
| 多页面共享 | @EnvironmentObject |
| 行为回传 | Closure |
| 路由传值 | NavigationStack |
| fullScreenCover | init / Binding / ViewModel |
总结
SwiftUI 中不存在“跳转时传值”,只有 状态注入与数据流管理。
fullScreenCover、sheet、NavigationStack只是 UI 呈现方式,真正决定数据传递的是状态的设计与归属。