绑定是SwiftUI展示给我们的几个属性包装器之一,用于控制应用程序中的数据流。绑定为我们提供了类似于访问值类型的参考。本文,我们将了解如何以及何时使用绑定。我们将学习如何在SwiftUI中使用绑定时避免常见错误。
绑定是一种属性包装器类型,可以读取和写入数值。在SwiftUI中,我们有几种可能数据来源。它可以是EnvironmentObject,ObservedObject或State。所有这些属性包装器都提供了一个绑定的投影值。让我们看一个简单的例子。
import SwiftUI
struct ExampleView: View {
@State private var text = "Hello World."
var body: some View {
TextField("type something...", text: $text)
}
}
在这里,我们有一个State变量。我们还有一个TextField,它需要一个文本值的绑定。我们使用$符号访问状态属性包装器的预计值,该值是对属性包装器值的绑定。
让我们看一个更复杂的例子。我们将构建一个显示用户列表并允许我们编辑用户数据的应用程序。
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: 28),
.init(id: .init(), name: "John", age: 31),
.init(id: .init(), name: "Fred", age: 25)
]
}
struct PersonsView : View {
@ObservedObject var store: PersonStore
var body: some View {
NavigationView {
List {
ForEach(store.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)
}
}
}
}.navigationBarTitle(Text("Persons"))
}
}
}
请记住,绑定的值必须是值类型。这意味着它必须是枚举或结构。我看到人们有时使用类来描述EnvironmentObject或ObservedObject内的状态或条目,并注意到绑定无效。 Apple关于绑定的文档说:如果Value不是值语义,则未指定使用所得Binding的任何视图的更新行为.
struct EditingView: View {
@Environment(\.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.dismiss()
}
}
}.navigationBarTitle(Text(person.name))
}
}
如您在上面的示例中看到的,我们使用绑定将可写引用传递给人员结构。一旦我们在EditingView中修改了用户实例,SwiftUI就会更新PersonsView以遵守更改。
extension Sequence {
func indexed() -> Array<(offset: Int, element: Element)> {
return Array(enumerated())
}
}
您可以看到我使用索引函数生成了一个元组数组,该数组提供了元素及其索引。它使我可以读取项目以将其渲染到位,并使用项目的索引访问绑定。
通常,我们使用真实来源的预计值访问绑定。在本节中,我们将讨论创建绑定的另一种方法。绑定是数据和访问数据的视图之间的双向连接。 SwiftUI提供了一种使用getter和setter闭包构造绑定的方法。在这种情况下,我们负责计算这些闭包内部的值。很难想象我们可以在哪里使用它,但是它在类似Redux的状态容器中可以很好地发挥作用。
typealias Reducer = (inout State, Action) -> Void
final class Store: ObservableObject {
@Published private(set) var state: State
private let reducer: Reducer
init(initialState: State, reducer: Reducer) {
self.state = initialState
self.reducer = reducer
}
func send(_ action: Action) {
reducer(&state, action)
}
}
在这里,我们有一个存储概念,可以保存应用程序的整个状态。状态的所有变化都来自单向流。 Reducer是我们可以改变应用程序状态的唯一位置。通过使用计算绑定,我们可以提供对状态的只读访问,并通过将操作发送给reducer来重塑单向流。
extension Store {
func binding(
for keyPath: KeyPath,
transform: @escaping (Value) -> Action
) -> Binding {
Binding(
get: { self.state[keyPath: keyPath] },
set: { self.send(transform($0)) }
)
}
}
如您所见,我们生成了一个计算绑定,该绑定读取状态的一部分,并在需要时通过reducer发出操作以修改状态。例如,当您有一个描述某些绑定到应用程序状态的复选框的设置屏幕时,可能需要这种类型的绑定
创建绑定的另一种方法是静态常量函数。此函数使我们可以创建一个提供值的绑定,但忽略其上的任何突变。换句话说,它为提供的值生成一个不变的绑定。
import SwiftUI
struct ExampleView: View {
var body: some View {
TextField("type something...", text: Binding.constant("Hello!"))
}
}
今天,我们学习了另一个出色的工具来控制SwiftUI中的数据流。绑定可以是比其他工具更复杂的工具,但是我相信我们涵盖了在SwiftUI中有效使用绑定的所有必要条件。
QQ:3365059189
SwiftUI技术交流QQ群:518696470