SwiftUI:为什么@State只适用于结构体

SwiftUI的State属性包装器是为当前视图本地的简单数据设计的,但是只要您想在视图之间共享数据,它就不再有用。

让我们用一些代码来分解这个问题—这里有一个存储用户名字和姓氏的结构体:

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

现在,我们可以在SwiftUI视图中通过创建@State属性并将其附加到$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)
        }
    }
}

一切正常:SwiftUI足够聪明,能够理解一个对象包含了我们的所有数据,并且在任何一个值发生更改时都会更新UI。在幕后,实际发生的情况是,每当我们的结构体中的值更改整个结构体时,它就像每次我们为新用户的名字或姓氏输入值时一样(创建了一个新的结构体)。听起来可能很浪费,但实际上速度非常快。

之前我们研究了类和结构体之间的区别,我提到了两个重要的区别。首先,结构总是有唯一的所有者,而对于类,多个对象可以指向同一个值。其次,类不需要在更改其属性的方法之前使用mutating关键字,因为您可以更改常量类的属性。

实际上,这意味着,如果我们有两个SwiftUI视图,并且我们将同一个结构体发送给它们,那么它们实际上都有该结构体的唯一副本;如果其中一个更改了它,那么另一个将看不到该更改。另一方面,如果我们创建一个类的实例并将其发送到两个视图,它们将共享更改。

对于SwiftUI开发人员来说,这意味着如果我们想要在多个视图之间共享数据——如果我们想要两个或多个视图指向同一个数据,以便在一个更改时它们都得到这些更改——我们需要使用类而不是结构体。

所以,请将User结构体更改为类。由此:

struct User {

改为:

class User {

现在再运行一次程序,看看你能想到什么。

SP:它不再工作了。当然,我们可以像以前一样键入文本字段,但是上面的文本视图不会改变。

当我们使用@State时,我们要求SwiftUI监视属性的更改。因此,如果我们更改字符串、翻转布尔值、添加到数组等,则属性已更改,SwiftUI将重新调用视图的body属性。

User是一个结构体时,每次我们修改该结构体的属性时,Swift实际上是在创建该结构的新实例。@State能够发现这个变化,并自动重新加载我们的视图。现在我们有了一个类,这种行为不再发生:Swift可以直接修改值。

还记得我们如何为修改属性的结构体方法使用mutating关键字吗?这是因为,如果我们将结构体的属性创建为变量,但结构体本身是常量,则无法更改属性——Swift需要能够在属性更改时销毁和重新创建整个结构体,而常量结构则不可能这样做。类不需要mutating关键字,因为即使类实例标记为常量Swift,仍然可以修改变量属性。

我知道这听起来非常理论化,但这里有个问题:既然User是一个类,那么属性本身不会改变,所以@State不会注意到任何事情,也无法重新加载视图。是的,类中的值正在更改,但是@State不监视这些值,所以实际上发生的情况是,类中的值正在更改,但视图不会重新加载以反映该更改。

为了解决这个问题,是时候抛弃@State了。相反,我们需要一个更强大的属性包装器,名为@ObservedObject——让我们现在来看一下…

译自 Why @State only works with structs

Previous: 里程碑:项目4 - 6 Hacking with iOS: SwiftUI Edition Next: SwiftUI:使用@ObservedObject共享状态

赏我一个赞吧~~~

你可能感兴趣的:(SwiftUI:为什么@State只适用于结构体)