通过SwiftUI的数据流
1、数据流工具与哪些?
Property、@State、@Binding、@Environment、BindableObject
2、SwiftUI框架会帮你管理依赖
当一个属性被@State修饰变成状态属性时,SwiftUI会为你分配永久存储,并将管理状态属性关联的UI,一旦状态属性发生变化,SwiftUI就会更新UI。注意:状态属性是一个真实数据源,或称事实源。
SwiftUI会在状态属性发生变更时更新UI
比较下面播放按钮封装前后的代码
播放按钮封装前
封装后的错误写。因为使用状态我们就会为 isPlaying 建立了一个新数据源,这样我们必须和父级 PlayerView 的状态保持同步,这不是我们想要的效果,我们想做的是让它成为可重复使用的组件,所以这个视图不会拥有一个真实数据源,它只能读取一个值,然后改写它
处理这种情况的工具叫做绑定(Binding),通过绑定给数据源定义一个清楚的依赖,而不拥有它。并且不需要提供给初始值,因为绑定可以从状态中获取
注意:给拥有绑定的视图传递参数时,参数前必须用美元符号,表示从一个状态生成一个绑定
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,但是从出栈到进栈 传递模型会比较冗长
全部使用ObjectBinding构建APP,传递冗长
这时候就需要 EnvironmentObject 了,这真的很方便,不直接在层级间传输数据。
不直接在层级间传输数据,更加方便
8、真实数据源的选择
前者适合本地视图的数据,一个值类型。后者适合你控制的数据
9、小结
使用美元符号驱使源。仔细了解你的数据,尽量减少真实数据源,构建可重复使用组件。