@State — 是什么、用途和在 SwiftUI 中的使用

作者: IT Sectr 发布日期: 2026-06-19 阅读时间: 7 分钟

@State 是 SwiftUI 中的 Property Wrapper,用于管理单个视图内的局部状态。SwiftUI 会在每次 @State 属性变化时自动重绘视图,使界面无需手动调用更新即可实现响应式。Apple Developer Documentation (2025) 指出,@State 推荐用于属于单个视图的简单类型和结构。@State 是为 SwiftUI 界面添加交互性的最简单方式。

要点

  • @State — 属于单个视图的局部状态的 Property Wrapper
  • 自动更新 — SwiftUI 在 @State 属性变化时重新执行 body
  • 简单类型 — @State 适用于 String、Int、Bool、enum 和结构体
  • 不要传递给嵌套视图 — 从子组件修改时请使用 @Binding
  • private — @State 属性始终使用 private 修饰符声明

SwiftUI 中的 @State 是什么?

@State 是 SwiftUI 中内置的 Property Wrapper,允许视图存储和跟踪自己的状态。当 @State 值发生变化时,SwiftUI 会自动重绘视图,重新调用 body 属性。这是 SwiftUI 中响应式编程的基础:开发者声明状态,框架负责界面的同步。

@State 在堆上创建一个由 SwiftUI 管理的存储区域。这个区域是持久化的——它能在每次渲染时发生的视图结构重复初始化中存活下来。SwiftUI 使用视图标识符(基于在层次结构中的位置生成)将 @State 属性绑定到特定视图。因此,当父视图更新时,状态不会被重置。

重要限制:@State 仅适用于值类型(结构体、枚举、基本类型)。对于引用类型(类),请使用 @StateObject 或 @ObservedObject。如果将类分配给 @State 属性,SwiftUI 将无法检测到对象内部的变化——只能检测整个引用的替换。

@State 在底层是如何工作的?

SwiftUI 通过内部存储机制实现 @State。每个 @State 属性获得一个专用的内存单元,存储在视图的特殊存储容器中。当对 wrappedValue 进行写入时,SwiftUI 通过 didSet 通知其依赖图需要重绘。

swift
struct ContentView: View {
    @State private var name: String = "User"
    @State private var isLoggedIn: Bool = false

    var body: some View {
        VStack {
            Text("你好,\(name)")
            Button(isLoggedIn ? "退出登录" : "登录") {
                isLoggedIn.toggle()
            }
        }
    }
}

在示例中有两个 @State 属性:name(String)和 isLoggedIn(Bool)。调用 isLoggedIn.toggle() 时,SwiftUI 将 ContentView 标记为需要更新,并在下一个渲染周期中重新执行 body。关键点:@State 属性始终使用 private 修饰符声明——这表示状态仅属于当前视图,不应从外部直接修改。

为了观察变化,SwiftUI 使用 Combine 中的 CurrentValueSubject。每个 @State 属性创建一个隐藏的发布者,在每次变化时通知系统。这使得 SwiftUI 只需重绘最小必要视图集,避免完全更新层次结构。

何时在项目中使用 @State

@State 最适合简单的局部状态:搜索文本框、模态窗口的布尔标志、设置开关、计数器、选中的列表项。如果值仅在一个视图及其嵌套组件(通过 @Binding)中使用,@State 是正确的选择。对于需要在视图关闭后仍然保留的状态(如表单数据),只要视图保持在层次结构中,@State 也是合适的。

  • 文本字段 — 使用 @State 存储 TextField 中输入的文本
  • 布尔标志 — 使用 @State 显示/隐藏模态窗口和 sheet
  • 元素选择 — 使用 @State 跟踪选中的标签页或行
  • 计数器 — 使用 @State 处理带递增/递减的数值
  • 中间计算 — 使用 @State 在视图内缓存结果

不要将 @State 用于应用程序的全局状态、网络数据缓存或在多个屏幕中使用的对象。这些用途应由 @StateObject 和 @EnvironmentObject 处理。@State 也不适合存储大量数据——每次变化时整个视图都会被重绘。

@State 和 @Binding:协同工作

@Binding 是父视图中 @State 与需要修改此状态的子视图之间的桥梁。父视图声明 @State,子组件通过 $ 投影获取 Binding。子视图中 Binding 的更改会自动更新父视图中的 @State——反之亦然。这确保了具有反馈可能的单向数据流。

swift
struct ParentView: View {
    @State private var text: String = ""

    var body: some View {
        ChildView(text: $text)
    }
}

struct ChildView: View {
    @Binding var text: String

    var body: some View {
        TextField("Enter text", text: $text)
    }
}

在代码中,ParentView 拥有 @State text,ChildView 接收 $text 作为 Binding。ChildView 内的 TextField 通过 text: $text 绑定到此 Binding。当用户在 TextField 中键入时,值通过 Binding 在 ChildView 中发生变化,从而触发 ParentView 中 @State 的更新。两个视图都使用新值重绘。

使用 @State 时的常见错误

最常见的错误——将分配给 @State 属性。如果您写 @State var model = MyClass(),SwiftUI 将无法跟踪类内部属性的变化——只能跟踪对象本身的替换。对于类始终使用 @StateObject。第二个常见问题——在声明 @State 时不使用 private 修饰符,这违反了状态封装原则。

直接将 @State 传递给子视图而不使用 $——另一个典型错误。如果您传递 TextField(text: text) 而不是 TextField(text: $text),子组件将收到普通字符串而非 Binding。TextField 中文本的更改不会与父 @State 同步。始终使用 $ 投影来传递 Binding。

第三个错误——对相关数据使用多个 @State 属性。如果多个值逻辑上构成一个整体(如表单字段),将它们合并为一个结构体和一个 @State。这简化了向子视图传递状态,并减少了单独的更新触发器数量。

在 SwiftUI 中使用 @State 的示例

@State 在大多数 SwiftUI 项目中用于基本交互。让我们看一个登录表单的示例,其中 @State 管理文本字段和加载状态。这种模式在每个应用程序中都能找到——从简单的笔记到复杂的企业解决方案。

swift
struct LoginView: View {
    @State private var email: String = ""
    @State private var password: String = ""
    @State private var isLoading: Bool = false
    @State private var errorMessage: String?

    var body: some View {
        Form {
            TextField("Email", text: $email)
            SecureField("Password", text: $password)
            Button("登录") {
                login()
            }.disabled(isLoading)
        }
    }

    private func login() {
        isLoading = true
        // 执行网络请求
    }
}

在示例中有四个 @State 属性:email 和 password 用于表单字段,isLoading 用于加载指示,errorMessage 用于显示错误。每个属性独立管理自己那部分界面。当 isLoading 改变时,按钮通过 disabled(isLoading) 自动锁定——无需手动更新 UI。

常见问题

为什么 @State 用 private 声明?

@State 专用于特定视图的局部状态。private 修饰符确保其他组件无法直接修改它,从而破坏封装性。外部访问请使用 $ 投影。

@State 可以包含数组或字典吗?

可以,@State 支持数组和字典,因为它们是值类型。然而,当修改数组元素时,SwiftUI 会重绘整个视图。对于大型列表,使用 @StateObject 配合 @Published 更高效。

当给 Optional 类型的 @State 属性赋 nil 时会发生什么?

@State 能正确处理 Optional 类型。当赋值为 nil 时,SwiftUI 会检测到变化并重绘视图。这对于像 errorMessage: String? 这样的状态很方便,其中 nil 表示没有错误。

@State 在视图重新出现时如何表现?

@State 只要视图保持在层次结构中就会保留值。如果视图从层次结构中移除并重新添加,@State 将使用默认值重新初始化。持久化请使用 @AppStorage。

@State 的变化可以动效吗?

可以,将变化包裹在 withAnimation 中:withAnimation(.easeInOut) { isExpanded.toggle() }。SwiftUI 会使用指定的动画类型对界面新旧状态之间的过渡进行动画处理。

总结

  • @State — 单个视图局部状态的 Property Wrapper,自动更新界面
  • 适用于简单类型:String、Int、Bool,以及结构体和枚举
  • 不适用于引用类型(类)——请使用 @StateObject
  • 始终 private — 状态不应从外部直接修改
  • $ 投影 — 创建 Binding 以将修改权限传递给子视图
  • 多个 @State 在同一个视图中——独立状态的正常做法
  • withAnimation — 允许对 @State 属性的变化进行动画处理

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读