两种设计模式
每次在视图中读取数据时,都会在那个视图中创建一个依赖
每次数据发生变化时,视图也会发生变化
在视图层级中个,读取的每一条数据,都只有一个数据源
例如,在同级视图中,用同一个数据,你的实现是这样的:
03:40
这种实现会在不同的View中维护同一个数据来源,这一个过程很容易造成数据同步错误导致的BUG。不妨改成这样来实现:
4:10
@State
1
@State var isPlaying: Bool = false
@State告诉系统
isPlaying
是一个可以变化的值,视图也会随之改变9:08
当你声明一个
@State
变量时,SwiftUI框架会为之分配永久存储,SwiftUI可以在变量发生变化时,重新渲染其关联的视图和其所有的SubvIew(只会重新渲染发生改变的地方)11:02
在SwiftUI中,数据流向是单一向的
@Binding
保持不同视图的数据同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24struct PlayerView: View {
let episode: Episode
@State private var isPlaying: Bool = false
var body: some View {
VStack {
Text(episode.title).foregroundColor(isPlaying ?.white : .gray)
Text(episode.showTitle).font(.caption).foregroundColor(.gray)
PlayButton(isPlaying: $isPlaying)
}
}
}
struct PlayButton: View {
// 这里的isPlaying和👆的是同一个,所以无需同步数据
@Binding var isPlaying: Bool
var body: some View {
Button(action: {
self.isPlaying.toggle()
}) {
Image(systemName: isPlaying ? "pause.circle" : "play.circle")
}
}
}Publisher
19:45
在SwiftUI中,使用Publisher来表述这些例如Timer和通知的外部事件。
1
2
3
4// 接受来自currentTimePublisher的事件
.onReceive(PodcastPlayer.currentTimePublisher, perform: { newCurrentTime in
self.currentTime = newCurrentTime
})BindableObject
1
2
3
4
5
6
7
8
9
10
11
12
13class PodcastPlayerStore: BindableObject {
var didChange = PassthroughSubject<Void, Never>()
// ...
func advance() {
currentEpisode = nextEpisode
currentTime = 0.0
didChange.send()
}
}PassthroughSubject是一个Publisher,SwiftUI会订阅这个Publisher,来更新视图层级
@ObjectBinding
当使用
@ObjectBinding
声明模型时,SwiftUI会识别出这个属性,并给它设置依赖,一旦模型发生变化,框架就会自动明白什么时候刷新视图。1
2
3
4
5
6struct MyView: View {
@ObjectBinding var model: MyModelObject
// ...
}
MyView(model: modelInstance)26:08
所有声明@ObjectBindng模型的视图,都会自动订阅模型,并在模型发生改变时,刷新视图。(即:依赖自动追踪)
创建一个间接依赖
上面声明的依赖关系,属于视图直接依赖,接下来看看如何生成一个视图的间接依赖
27:01