SwiftUI 中的这些注解(专业术语叫 Property Wrappers / 属性包装器)是驱动界面刷新的核心引擎。

你可以把它们简单理解为:告诉 SwiftUI 某个变量的“身份”和“刷新规则”。

为了方便记忆,我把它们分为三大类:基础值类型引用类型(对象)环境与存储


第一类:基础值类型 (Int, String, Bool, Struct)

用于管理简单的 UI 状态,比如开关、输入框文字、数字计数。

1. @State (私有状态)

  • 身份: “房主”
  • 作用: View 内部私有的、由 View 自己创建和管理的变量。
  • 刷新规则: 值改变 -> 当前 View 重新渲染。
  • 场景: 按钮点击次数、弹窗是否显示、输入框的内容。

    struct CounterView: View {
        @State private var count = 0 // View 自己管理这个状态
    
        var body: some View {
            Button("Tap me: \(count)") { count += 1 }
        }
    }

2. @Binding (绑定 / 引用)

  • 身份: “传声筒”
  • 作用: 它不存数据,它只是指向别人(通常是父视图)的 @State。它实现了双向绑定:子视图改了,父视图的数据也会变。
  • 场景: 开关组件(Toggle)、输入框组件。
  • 用法: 传参时需要加 $ 符号。

    struct ParentView: View {
        @State private var isOn = false
        var body: some View {
            // 把 $isOn 传给子视图,建立连接
            ToggleView(isOn: $isOn) 
        }
    }
    
    struct ToggleView: View {
        @Binding var isOn: Bool // 接收连接,不设初值
        var body: some View {
            Toggle("Switch", isOn: $isOn) // 修改这里,ParentView 的 isOn 也会变
        }
    }

第二类:引用类型 (Class / ViewModel)

用于管理复杂的逻辑和数据,通常遵循 ObservableObject 协议。

3. @StateObject (对象持有者)

  • 身份: “钉子” (Owner)
  • 作用: 负责初始化持有一个 ViewModel。即使 View 刷新,这个对象也不会死。
  • 场景: 页面创建自己的 ViewModel 时使用。
    @StateObject var vm = MyViewModel() // ✅ 这里有 = 号

4. @ObservedObject (对象观察者)

  • 身份: “眼镜” (Observer)
  • 作用: 观察外部传入的 ViewModel。如果 View 刷新,它不保证对象存活(所以不能在这里初始化)。
  • 场景: 子视图接收父视图传进来的 ViewModel。
    @ObservedObject var vm: MyViewModel // ❌ 这里通常没有 = 号

5. @EnvironmentObject (全局/环境对象)

  • 身份: “广播接收器”
  • 作用: 不需要一层层传参,直接从“空气”(环境)里抓取对象。前提是祖先视图必须先用 .environmentObject(vm) 注入过。
  • 场景: 用户登录信息、全局主题设置、像你的 SyncSettingsViewModel 这种跨页面共享的数据。
    @EnvironmentObject var settings: SyncSettingsViewModel

6. @Published (发布者)

  • 位置: 写在 class (ViewModel) 内部。
  • 作用: 告诉系统:“当这个属性变化时,所有观察我的 View 都要刷新”。
    class MyViewModel: ObservableObject {
        @Published var items: [String] = [] // items 变了,界面就变
    }

第三类:持久化与环境系统

7. @AppStorage (UserDefaults 包装器)

  • 作用: 自动读写 UserDefaults。修改变量会自动保存到本地,且自动刷新 UI。
  • 场景: 简单的设置项(如你的 iCloudEnabled)。
    @AppStorage("username") var username: String = "Guest"

8. @Environment (系统环境值)

  • 作用: 读取系统提供的环境变量。
  • 场景: 获取当前是深色/浅色模式、关闭当前弹窗(dismiss)、获取核心数据上下文(managedObjectContext)。
    @Environment(\.colorScheme) var colorScheme
    @Environment(\.dismiss) var dismiss // 调用 dismiss() 关闭页面

⚡️ 总结:我该用哪个?(决策速查表)

你的数据类型 数据来源 推荐注解
Int, Bool, String 当前 View 自己产生 @State
Int, Bool, String 父 View 传下来的,且需要修改 @Binding
Int, Bool, String 父 View 传下来的,只读 普通变量 let (无需注解)
Object (Class) 当前 View 新建 (= ViewModel()) @StateObject
Object (Class) 父 View 传参 (: ViewModel) @ObservedObject
Object (Class) 隔代传递 / 全局共享 @EnvironmentObject
简单设置 需要存到 UserDefaults @AppStorage

💡 补充:iOS 17+ 的新变化 (Observation 框架)

如果你在看最新的 SwiftUI 教程(iOS 17+),你会发现上面的逻辑被简化了:

  • 不再需要 ObservableObject@Published,直接用宏 @Observable 标记 Class。
  • View 里不再区分 StateObject/ObservedObject,统一用 @State 或普通 let/var
  • 这种新写法是未来的趋势,但目前为了兼容 iOS 16/15,上面的旧写法依然是主流。

你想看一个把这些都串起来的完整代码示例吗?