(一)简答题(15分):
(1)请区分以下概念——
@State、@Binding、ObservableObject、@ObservedObject、@EnvironmentObject
本文教你区分这几个 SwiftUI 中常用的数据状态和绑定方法。
SwiftUI 数据状态和绑定
| 方案 | 说明 | 备注 |
|---|---|---|
@State & @Binding | 提供 View 内部的状态存储 | 应该是被标记为 private 的简单值类型,仅在内部使用。 |
ObservableObject & @ObservedObject | 针对跨越 View 层级的状态共享 | 处理更复杂的数据类型,在数据变化时触发界面刷新。 |
@EnvironmentObject | 对于 “跳跃式” 跨越多个 View 层级的状态 | 更方便地使用 ObservableObject,以简化代码。 |
@State & @Binding
Parent view:
struct ParentView: View {
@State private var foo: Bool = false
var body: some View {
VStack(spacing: 25) {
Text("foo in Parent: \(self.foo ? "✔️" : "❌")")
ChildView(foo: $foo)
}
}
}
Child view:
struct ChildView: View {
@Binding var foo: Bool
var body: some View {
Button("toggle foo from Child") {
self.foo.toggle()
}
}
}
运行效果:
@ObservedObject
Model:
class Model: ObservableObject {
@Published var foo: Bool = false
}
注意,@Published 是个 internal 的。在 Xcode 工程里没事,但在 Playground 的 Sources 里写是必须 public 的,这时可以采用下面这种手动处理的方法:
import Combine
public class Model: ObservableObject {
public let objectWillChange = PassthroughSubject<Void, Never>()
public var foo: Bool = false {
willSet { objectWillChange.send() }
}
public init() {}
}
let objectWillChange = PassthroughSubject<Void, Never>()用来实现ObservableObject协议;- 给每个需要
@Published的变量加willSet { objectWillChange.send() }。
Parent View:
public struct ParentView: View {
@ObservedObject var model: Model = Model()
public var body: some View {
VStack(spacing: 25) {
Text("foo in Parent: \(self.model.foo ? "✔️" : "❌")")
ChildView(model: model)
}
}
public init() {}
}
Child View:
public struct ChildView: View {
var model: Model
public var body: some View {
Button("toggle foo from Child") {
self.model.foo.toggle()
}
}
public init(model: Model) {
self.model = model
}
}
这里其实是利用 class 的“传引用”特性来让子访问到父的 model 的,没什么特殊的。
@EnvironmentObject
沿用上面 ObservableObject 的 Model:
class Model: ObservableObject {
@Published var foo: Bool = false
}
Parent View:
public struct ParentView: View {
@EnvironmentObject var model: Model
public var body: some View {
VStack(spacing: 25) {
Text("foo in Parent: \(self.model.foo ? "✔️" : "❌")")
ChildView()
}
}
public init() {}
}
Child View:
public struct ChildView: View {
@EnvironmentObject var model: Model
public var body: some View {
Button("toggle foo from Child") {
self.model.foo.toggle()
}
}
public init() {}
}
调用 Parent 的 View:
ParentView().environmentObject(Model())
用 EnvironmentObject 让所有子都访问到父的 Model,可以避免逐层手动传递对象。
完整源码实现:SwiftUIDataStateAndBinding.playground。
参考
王巍 (@onevcat)《SwiftUI 与 Combine 编程》Chapter 3:数据状态和绑定