paint-brush
SwiftUI 的 5 个主要属性包装器以及如何有效地使用它们经过@tseitlin
3,473 讀數
3,473 讀數

SwiftUI 的 5 个主要属性包装器以及如何有效地使用它们

经过 Mykhailo Tseitlin 9m2023/05/31
Read on Terminal Reader

太長; 讀書

SwiftUI 是一个用于在 iOS 和 macOS 上构建用户界面的框架。使用 SwiftUI,您可以描述您的界面应该做什么和看起来像什么,框架会处理剩下的事情。 SwiftUI 有五个主要的属性包装器:@State、@Binding、@ObservedObject、@StateObject 和@EnvironmentObject。
featured image - SwiftUI 的 5 个主要属性包装器以及如何有效地使用它们
Mykhailo Tseitlin  HackerNoon profile picture
0-item
1-item


嘿!我想向您介绍SwiftUI,这是一个用于在 iOS 和 macOS 上构建用户界面的框架。它使用起来非常方便,因为它采用声明式编程方法。使用 SwiftUI,您可以描述您的界面应该做什么和看起来像什么,框架会处理剩下的事情。


SwiftUI 的关键元素之一是属性包装器的使用。这些是允许您为属性提供附加逻辑的功能元素。


SwiftUI 有五个主要的属性包装器:

  1. @状态

  2. @捆绑

  3. @ObservedObject

  4. @StateObject

  5. @环境对象


他们将成为你发展中最好的朋友。


@状态


@State允许您创建可以更改的属性,并在必要时根据这些更改更新界面。例如,如果你想创建一个在按下时改变颜色的按钮,你可以创建一个@State 变量来存储颜色并将其添加到按钮:


 struct MyButton: View { @State var buttonColor = Color.blue var body: some View { Button("Press me!") { buttonColor = Color.red } .background(buttonColor) } }


@捆绑


@Binding允许您在代码的另一部分中使用存储在代码的一部分中的值。它通常在 SwiftUI 中用于将值从一个视图传递到另一个视图,从而允许它们相互交互。例如,假设我们有两个视图 - 一个带有文本字段,另一个带有按钮。我们希望文本字段在用户按下按钮时更新。为此,我们可以使用@Binding:


 struct ContentView: View { @State private var text = "" var body: some View { VStack { TextField("Enter text", text: $text) Button("Update text") { text = "New text" } SecondView(text: $text) } } } struct SecondView: View { @Binding var text: String var body: some View { Text(text) } }


在此示例中, @Binding用于将值从$text (在ContentView中)传递到text (在SecondView中),因此当用户按下按钮时,文本字段将更新并显示新文本。


@ObservedObject


@ObservedObject用于标记观察到的并且可以根据外部数据变化而变化的属性。该属性包装器订阅符合ObservableObject 协议的对象的变化,并在数据发生变化时自动更新接口的相关部分。下面是使用@ObservedObject的简单示例:

 class UserData: ObservableObject { @Published var name = "John" } struct ContentView: View { @ObservedObject var userData = UserData() var body: some View { VStack { Text("Hello, \(userData.name)!") TextField("Enter your name", text: $userData.name) } } }


在此示例中,我们创建了一个名为 UserData 的类,其中包含一个 @Published 名称。在 ContentView 结构中,我们使用 @ObservedObject 创建一个名为 userData 的属性,类型为 UserData。我们在文本字段中显示 userData.name 的值并将其输出到屏幕上。


当用户更改文本字段中的值时, SwiftUI 会自动更新界面的相应部分,因为名称属性已使用@Published 发布和观察。这意味着我们不需要自己的代码来更新界面,我们允许 SwiftUI 为我们做这件事。


注意:如果您不知道,@Published 是来自 Combine 框架的属性包装器,可以添加到类或结构属性中,它会自动向订阅它的任何人发送对该属性值的任何更改的通知.换句话说,它是可以跟踪更改的属性的辅助属性。


@StateObject


@StateObject是一个属性包装器,用于初始化类对象并将其存储在 SwiftUI 的视图状态中。这意味着只要视图存在,对象就会被存储,并与它一起被销毁。通常,对于多个视图(而不是一个)所需的类对象,使用@StateObject更为实用例如:


 class UserData: ObservableObject { @Published var name = "John" @Published var age = 30 } struct ContentView: View { @StateObject var userData = UserData() var body: some View { NavigationView { VStack { Text("Name: \(userData.name)") Text("Age: \(userData.age)") NavigationLink( destination: ProfileView(userData: userData), label: { Text("Edit Profile") }) } .navigationTitle("Home") } } } struct ProfileView: View { @ObservedObject var userData: UserData var body: some View { Form { TextField("Name", text: $userData.name) Stepper("Age: \(userData.age)", value: $userData.age) } .navigationTitle("Profile") } }


在此示例中, UserData是一个类的对象,该类包含多个可在多个视图中使用的属性。该类被标记为ObservableObject ,因此它可以与@StateObject@ObservedObject一起使用。


ContentView 中,我们使用@StateObject创建一个新的UserData对象来保存不同视图之间的转换之间的状态。在这种情况下, ContentView显示用户数据,将其可视化,并包含指向可用于编辑用户数据的另一个视图(ProfileView)的链接。


ProfileView中,我们使用@ObservedObject访问同一个UserData对象来修改用户数据。当用户更改数据时,它会在ContentView中自动更新,因为使用了相同的UserData对象。


注意:如果需要从一个视图观察类对象的变化,请使用@ObservedObject;如果需要保存影响多个视图显示的类对象的状态,请使用@StateObject。


如果对多个视图中需要的对象使用@ObservedObject 而不是@StateObject,每个视图都会有自己的对象实例,这会导致视图之间的数据同步出现问题。因此,在这种情况下,最好使用@StateObject。


@环境对象


@EnvironmentObject是一个属性包装器,用于通过 SwiftUI 视图层次结构传递数据对象。它允许从属于环境容器的 SwiftUI 层次结构中的任何视图(例如,场景、视图、应用程序等)访问数据对象。例如,假设我们有一个任务列表管理应用程序。我们可以有一个包含任务列表和创建新任务的能力的根ContentView 。为此,我们创建了一个单独的TaskListView视图,用于显示任务列表和一个用于添加新任务的按钮。添加新任务后,用户应该被重定向到添加任务屏幕,因此我们创建一个单独的AddTaskView视图。


要将UserManager对象传递给所有三个视图,我们可以在ContentView中创建它的实例,然后将它作为参数传递给TaskListViewAddTaskView 。但是,如果我们决定添加更多嵌套视图,这可能会成为一个问题,因为我们需要通过许多中间视图来传递UserManager


取而代之的是,我们可以使用@EnvironmentObject通过视图层次结构向下传递UserManager 。这样,所有需要访问UserManager 的视图都可以简单地将其声明为@EnvironmentObject并根据需要使用它。

 struct TaskManagerApp: App { @StateObject var userManager = UserManager() var body: some Scene { WindowGroup { ContentView() .environmentObject(userManager) } } } struct ContentView: View { var body: some View { NavigationView { TaskListView() } } } struct TaskListView: View { @EnvironmentObject var userManager: UserManager var body: some View { List(userManager.tasks) { task in TaskRow(task: task) } .navigationBarTitle("Tasks") .navigationBarItems(trailing: Button(action: { // Navigate to AddTaskView }) { Image(systemName: "plus") } ) } } struct AddTaskView: View { @EnvironmentObject var userManager: UserManager var body: some View { // Add new task using userManager } }


因此,现在UserManager对象将通过@EnvironmentObject自动传递给TaskListViewAddTaskView 。请注意,我们可以在一个视图中修改UserManager的状态,并且更改会自动反映在另一个视图中。



本文介绍了基本的 SwiftUI 属性包装器: @State、@Binding、@ObservedObject、@StateObject、@EnvironmentObject。


这些属性包装器构成了在 SwiftUI 中处理应用程序状态的基础。将本文用作备忘单,让基本属性包装触手可及,这是使用 SwiftUI 开发应用程序所必需的。通过应用这些知识,您将能够构建具有动态变化状态的更复杂的用户界面,并将模型中的数据集成到 SwiftUI 中。