通过SwiftUI的数据流
1、数据流工具与哪些?
Property、@State、@Binding、@Environment、BindableObject
2、SwiftUI框架会帮你管理依赖
当一个属性被@State修饰变成状态属性时,SwiftUI会为你分配永久存储,并将管理状态属性关联的UI,一旦状态属性发生变化,SwiftUI就会更新UI。注意:状态属性是一个真实数据源,或称事实源。
比较下面播放按钮封装前后的代码
3、SwiftUI中管理数据的工具
Publisher,来自 Combine框架,是一个随时间处理数据的统一声明式API。
- 是一个单例
- 主线程使用.receive(on:)
Publisher使用
struct PlayerView : View {
let episode: Episode
@State private var isPlaying: Bool = true
@State private var currentTime: TimeInterval = 0.0
var body: some View {
VStack {
Text(episode.title).foregroundColor(isPlaying ? .white : .gray)
Text(episode.showTitle).font(.caption).foregroundColor(.gray)
PlayButton(isPlaying: $isPlaying)
Text("\(currentTime, formatter: currentTimeFormatter)")
}
.onReceive(PodcastPlayer.currentTimePublisher) { newCurrentTime in
self.currentTime = newCurrentTime
}
}
}
4、BindingObject 协议
class PodcastPlayerStore : BindableObject {
//遵守协议实现didChange属性
var didChange = PassthroughSubject<Void, Never>()
var currentTime: TimeInterval
var isPlaying: Bool
var currentEpisode: Episode
func advance() {
currentEpisode = nextEpisode
currentTime = 0.0
// 通知订阅和播放器状态改变changed
didChange.send()
}
func skipForward() { ... }
func skipBackward() { ... }
}
5、在BindingObject上创建依赖
我们这么做时,每个有proper wrapper(属性包装器)的视图都会自动订阅BindingObject的变化,也意味着我们实现了依赖自动追踪又不需要失效和同步了。
6、另外一种工具创建依赖,即创建间接依赖 。
@EnvironmentObject,它可以推动数据一路向下流过视图层级。可以真正的把BindingObject写入Environment。
通过它,我们可以更新我们的播客播放器,如下:
struct PlayerView : View {
@EnvironmentObject var player: PodcastPlayerStore
var body: some View {
VStack {
Text(player.currentEpisode.title)
.foregroundColor(isPlaying ? .white : .gray)
Text(player.currentEpisode.showTitle)
.font(.caption).foregroundColor(.gray)
PlayButton(isPlaying: $player.isPlaying)
Text("\(player.currentTime, formatter: playheadTimeFormatter)")
}
}
}
7、EnvironmentObject和ObjectBinding使用选择
实际上你可以使用 ObjectBinding 构建整个 App,但是从出栈到进栈 传递模型会比较冗长
这时候就需要 EnvironmentObject 了,这真的很方便,不直接在层级间传输数据。
8、真实数据源的选择
9、小结
使用美元符号驱使源。仔细了解你的数据,尽量减少真实数据源,构建可重复使用组件。