原文: Managing Data Flow in SwiftUI
所有App都有要呈现或变化的数据。数据在使用 SwiftUI 的App中起着至关重要的作用。 SwiftUI 中的每个视图都只是某种状态(State)的函数,其中State就是我们的数据。
从本地/远程存储中获取数据
今天我们将使用核心 SwiftUI 概念构建一个小应用程序,它,如 Binding 和 ObservableObject。假设您在应用程序上工作,它有两个主要职责:
- 从本地或远程存储中获取并显示员工列表
- 编辑选定员工的个人信息
让我们从描述我们的模型层(model layer) 开始
import SwiftUI
import Combine
struct Person: Identifiable {
let id: UUID
var name: String
var age: Int
}
final class PersonStore: ObservableObject {
@Published var persons: [Person] = [
.init(id: .init(), name: "Majid", age: 27),
.init(id: .init(), name: "John", age: 31),
.init(id: .init(), name: "Fred", age: 25),
]
}
Person结构遵循 Identifiable协议, Identifiable的唯一要求就是Hashable id字段.我们通过将id定义为UUID来实现它.我们也可以使用Int代替UUID.
接下来我们将实现PersonStore类,它为我们的视图提供数据。PersonStore 类型符合 ObservableObject ,它将允许 SwiftUI 在任何 @Published 字段更改时刷新视图。
现在我们来看看PersonListView
struct PersonView: View {
@ObservedObject var store: PersonStore
var body: some View {
NavigationView {
List(store.persons) { person in
VStack(alignment: .leading) {
Text(person.name)
.font(.headline)
Text("Age: (person.age)")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}.navigationBarTitle(Text(person.name))
}
}
我们使用 List 组件来呈现一个 Person 结构数组。 List 中的每一行都包含 VStack,其中包含两个 Text 组件,分别表示 Person 的姓名和年龄。一旦 List 出现,我们就在 store 对象上调用 fetch 方法。您还记得,我们的 PersonStore 对象会通知 SwiftUI 数据更改,而 SwiftUI 会重建视图以呈现新数据。
编辑
下一步是创建一个新视图,该视图允许我们编辑所选 Person 的个人信息。我们将使用 Form 组件来显示数据输入的良好表单。您可以查看“使用 SwiftUI 构建表单”,以了解有关 Form 组件及其优点的更多信息。让我们深入研究代表编辑视图的代码。
struct EditingView: View {
@Enviroment(.presentationMode) var presentation
@Binding var person: Person
var body: some View {
Form {
Section(header: Text("Personal information")) {
TextField("type something...", text: $person.name)
Stepper(value: $person.age) {
Text("Age: (person.age)")
}
}
Section {
Button("Save") {
self.presentation.wrappedValue.dismisss()
}
}
}.navigationBarTitle(Text(person.name))
}
}
在这里,我们对选定的person使用Binding。 Binding 属性包装器允许传递对值类型的引用。通过使用 Binding 属性,EditingView 可以读取和改变 Person 结构,但它不存储它的副本。我们使用这个 Binding 来改变 如果您想了解更多关于 SwiftUI 中可用的属性包装器,比如 @Binding, @Environment, @EnvironmentObject, @ObservedObject, 属性包装器,请移步至“理解SwiftUI的属性包装值”
现在让我们重构 PersonsView,通过将 Binding 传递给 EditingView 中选定的 Person 来支持编辑。
import Foundation
extension RandomAccessCollection {
func indexed() -> Array<(offset: Int, element: Element)> {
Array(enumerated())
}
}
import SwiftUI
struct PersonsView: View {
@ObservedObject var store: PersonStore
var body: some View {
NavigationView {
List {
ForEach(stored.persons.indexed(), id: .1.id) { index, person in
NavigationLink(destination: EditingView(person: self.$store.persons[index])) {
VStack(alignment: .leading) {
Text(person.name)
.font(.headline)
Text("Age: (person.age)")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
}
.onAppear(perform: store.fectch)
.navigationBarTitle(Text("Persons"))
}
}
}
这是我们App的屏幕截图
总结
今天我们在 SwiftUI 中构建了简单的 Master-Detail 流。我尝试向你展示了Bindings在SwiftUI中是多么有用.
你不需要去发布通知或者观察一个键值对来表明你的界面的变化,你只需要正确的使用SwiftUI提供的属性包装器.
再次,如果你想学习什么时候使用哪一个属性包装器,请看“理解SwiftUI的属性包装值”.