SwiftUI 传值方式系统整理

4 阅读1分钟

SwiftUI 传值方式系统整理

在 SwiftUI 中,所谓“传值”本质上是 状态(State)和数据流(Data Flow)在不同 View 之间的传播方式fullScreenCoversheetNavigationStack 只是视图呈现手段,本身并不负责数据传递。

本文从基础到工程化,系统整理 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

常与以下方式配合:

  • @Binding
  • ObservableObject

使用模式

  • 子 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
fullScreenCoverinit / Binding / ViewModel

总结

SwiftUI 中不存在“跳转时传值”,只有 状态注入与数据流管理

fullScreenCoversheetNavigationStack 只是 UI 呈现方式,真正决定数据传递的是状态的设计与归属。