通过SwiftUI的数据流——WWDC 2019

通过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()
    
    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、小结
使用美元符号驱使源。仔细了解你的数据,尽量减少真实数据源,构建可重复使用组件。


你可能感兴趣的:(通过SwiftUI的数据流——WWDC 2019)