Using @State with classes

在 SwiftUI 中,@State 属性包装器用于管理当前视图中的简单数据。当你想要在多个视图之间共享数据时,必须采取一些额外的步骤。

示例代码分析:

假设我们有一个 User 结构体来存储用户的名字和姓氏:


struct User {
    var firstName = "Bilbo"
    var lastName = "Baggins"
}


然后在 SwiftUI 视图中,我们使用 @State 属性包装器来创建一个 user 实例,并通过绑定($user.firstName 和 $user.lastName)将这些数据与界面控件连接起来:


struct ContentView: View {
    @State private var user = User()

    var body: some View {
        VStack {
            Text("Your name is \(user.firstName)\(user.lastName).")

            TextField("First name", text: $user.firstName)
            TextField("Last name", text: $user.lastName)
        }
    }
}


工作原理:

  1. SwiftUI 会监视 @State 属性包装器标记的数据变化。每当 user.firstName 或 user.lastName 发生变化时,视图就会重新渲染。
  2. @State 是通过创建一个新的结构体实例来实现这一点。每当你更改结构体中的数据时,整个结构体都会被重新创建。这种方式尽管看起来可能会浪费资源,但实际上是非常高效的。

结构体与类的区别:

在前面的代码中,我们使用了结构体 User,这是一个值类型(值传递)。如果你将其改为类,则变成了引用类型(引用传递),并且行为发生了变化。

改为类:

我们将 User 改为类后,问题就出现了:



struct User {

To this:

class User {


为什么不工作了?

  • 在结构体中,SwiftUI 通过创建新实例来跟踪数据的变化,这就使得视图能够更新。
  • 但是对于类来说,数据是按引用传递的。如果 user 变成了一个类实例,数据发生改变时,类本身并没有被重新创建,@State 不会知道这个变化,从而导致视图不刷新。

为什么会出现这个问题?

  • @State 会监听属性的变化,但是当使用类时,SwiftUI 并不会看到类实例本身发生变化,因为类是引用类型。类的属性虽然发生了变化,但由于 @State 不直接监控引用类型的属性,它不会触发视图刷新。

解决方案:使用 @Observable

要解决这个问题,我们需要使用 @Observable 宏来告诉 SwiftUI,这个类是可观察的,并且当它的属性发生变化时,视图需要刷新。改成以下代码:


@Observable
class User {


为什么 @Observable 有效?

  • @Observable 属性包装器让 SwiftUI 知道,类的实例是可以观察的。当类的属性发生变化时,SwiftUI 会自动刷新视图。
  • 通过 @Observable,即使是引用类型的 User 类,只要其中的属性发生变化,视图也会正确地重新渲染。

总之,使用 @Observable 可以让类成为可观察对象,从而保证当类内部数据发生变化时,SwiftUI 会重新渲染视图,避免了因为类的引用类型特性导致的视图不更新问题。



Review after registration

login page