阅读 158

[SwiftUI 100 天] 用 @EnvironmentObject 在 tab 间共享数据

译自 www.hackingwithswift.com/books/ios-s…

更多内容,欢迎关注公众号 「Swift花园」

喜欢文章?不如来个 🔺💛➕三连?关注专栏,关注我 🚀🚀🚀

用 @EnvironmentObject 在 tab 间共享数据

SwiftUI 的 environment 可以让我们以一种相当优雅的方式共享数据:任何视图都可以把数据发送到环境中,然后任何子视图都可以从环境中读出这些数据。更棒的是,如果一个视图改变对象,所有其他视图都会自动更新 —— 这是一种极为聪明的在大型应用中共享数据的方式。

我们的应用中有 TabView,它包含了三个 ProspectView 实例,我们需要它们是基于相同的共享数据运作的三个不同的视图。这是 SwiftUI 环境工作机制的绝佳示例:我们可以定义一个类来存储 prospect,然后用一组 prospect 注入环境,以便所有视图可以按需读取。

新建一个 Swift 文件,取名 Prospect.swift,把 Foundation 导入替换为 SwiftUI,然后添加下面的代码:

class Prospect: Identifiable, Codable {
    let id = UUID()
    var name = "Anonymous"
    var emailAddress = ""
    var isContacted = false
}
复制代码

是的,这里采用类而不是结构体是有意为之,因为这样能让我们直接修改类的实例,让它在所有视图之间同时更新。记住,SwiftUI 是自动处理变化的传播过程。

说到在多个视图间共享数据,SwiftUI 的 environment 有一大好处是它采用的是相同的 ObservableObject 协议,我们之前已经用它配合 @ObservedObject 属性包装器。这意味着,只要我们给属性标记 @Published 属性包装器 —— SwiftUI 自动为我们完成大部分工作。

把这个类添加到 Prospect.swift:

class Prospects: ObservableObject {
    @Published var people: [Prospect]

    init() {
        self.people = []
    }
}
复制代码

我们稍后回来修改这个类。

现在,我们要让所有的 ProspectView 实例共享单一的 Prospects 实例,以便它们都指向相同的数据。如果我们还是采用 UIKit 编写代码,那我需要大费口舌来解释实现这一点有多么困难,以及我们在确保所有变化合理传播这件事时要多么当心,但在 SwiftUI 中,我们只需要三步。

首先,我们添加一个属性到 ContentView,创建一个 Prospects 的单例:

var prospects = Prospects()
复制代码

其次,我们需要发送这个属性到 SwiftUI 的环境中,以便所有的子视图可以访问它。因为 tab 被看作是 tab view 的子视图,所以如果我们把这个属性添加到 TabView 的环境,那么所有的 ProspectsView 实例都将能够访问它。

把这个 modifier 添加到 ContentViewTabView

.environmentObject(prospects)
复制代码

接下来我们需要所有的 ProspectsView 在创建时从环境中读出这个对象。这里用到一个新的属性包装器,叫 @EnvironmentObject。把下面这个属性添加到 ProspectsView

@EnvironmentObject var prospects: Prospects
复制代码

以上就是全部的步骤 —— 我想没有比这个更简单的方式了。

重要: 当你是用 @EnvironmentObject 的时候,你是在显式地告诉 SwiftUI ,当视图创建时你的对象会存在于环境中。如果它不存在,你的应用将会立即崩溃 —— 因此,请千万小心,把环境对象当成一个隐式的未解包的可选型来处理。

很快我们将要添加代码实现通过扫描二维码来添加 prospect 的功能,现在我们要先添加一个导航栏,用于添加测试数据。

ProspectsViewbody 属性修改为这样:

NavigationView {
    Text("People: \(prospects.people.count)")
        .navigationBarTitle(title)
        .navigationBarItems(trailing: Button(action: {
            let prospect = Prospect()
            prospect.name = "Paul Hudson"
            prospect.emailAddress = "paul@hackingwithswift.com"
            self.prospects.people.append(prospect)
        }) {
            Image(systemName: "qrcode.viewfinder")
            Text("Scan")
        })
}
复制代码

现在你会在 ProspectsView 视图里看到一个 “扫描” 按钮,点击它,为三个视图同时添加一个人 —— 无论点的是哪个 tab 里的按钮,你都会看到 count 增加。


我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~

Swift花园微信公众号