这篇文章介绍了如何使用SwiftUI和UserDefault创建一个记录消费的app,并介绍了如何使用@ObservedObject共享SwiftUI状态。文章提供了ContentView.swift和AddRecording.swift的代码示例。
此内容根据文章生成,并经过人工审核,仅用于文章内容的解释与总结
投诉这是一个记录消费的一个app,主要练习的是Userdefult的使用。今天就先把前端部分完成,明天完成其他部分和优化。
与@ObservedObject共享SwiftUI状态
@State 只能监控 结构 的更改,很难监控到 类 的更改,这个时候需要使用@ObservedObject
如果你想使用一个类与SwiftUI数据-你会想要做的,如果该数据跨多个视图共享-然后SwiftUI给了我们两个属性包装是有用的:@ObservedObject和@EnvironmentObject。稍后我们将研究环境对象,但现在让我们集中关注观察到的对象。
这是一些创建User类的代码,并在视图中显示用户数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class User { var firstName = "Bilbo" var lastName = "Baggins" }
struct ContentView: View { @State private var user = User()
var body: some View { VStack { Text("Your name is \(user.firstName) \(user.lastName).")
TextField("First name", text: $user.firstName) TextField("Last name", text: $user.lastName) } } }
|
但是,该代码无法按预期工作:我们已使用标记了user属性@State,该属性旨在跟踪本地结构而不是外部类。结果,我们可以在文本字段中键入内容,但是上面的文本视图不会被更新。
为了解决这个问题,我们需要在类的有趣部分发生更改时告诉SwiftUI。“有趣的部分”是指应该导致SwiftUI重新加载正在观看我们班级的所有视图的部分–可能你的班级内部可能有很多属性,但是只有这种情况下,才应该暴露给更广阔的世界。
我们的User课程有两个属性:firstName和lastName。每当这两个更改中的任何一个更改时,我们都希望通知正在观看我们班级的所有视图发生了更改,以便可以重新加载它们。我们可以使用@Published属性观察器执行此操作,如下所示:
1 2 3 4
| class User { @Published var firstName = "Bilbo" @Published var lastName = "Baggins" }
|
@Published是或多或少的一半@State:它告诉Swift,只要这两个属性中的任何一个发生更改,它都应该向任何正在观看它们应该重新加载的SwiftUI视图发送一条通知。
这些视图如何知道哪些类可以发出这些通知?那是另一个属性包装器,@ObservedObject是它的另一半@State–它告诉SwiftUI监视类中是否有任何更改声明。
因此,将user属性更改为此:
1
@ObservedObject var user = User()
我删除了private那里的访问控制,但是是否使用它取决于你的使用情况–如果你打算与其他视图共享该对象,则将其标记为private只会引起混乱。
现在我们正在使用@ObservedObject,我们的代码将不再编译。这不是问题,事实上,它是预期的并且很容易修复:@ObservedObject属性包装器只能用于符合ObservableObject协议的类型。该协议没有任何要求,实际上意味着“我们希望其他事物能够监视此更改”。
因此,将User类修改为此:
1 2 3 4
| class User: ObservableObject { @Published var firstName = "Bilbo" @Published var lastName = "Baggins" }
|
我们的代码现在再编译,甚至更好,它现在实际上工作再次-你可以运行应用程序并看到文本视图更新时,无论是文本字段被改变。
代码
ContentView.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
|
import SwiftUI
struct ContentView: View { @State var windowShow = false @ObservedObject var expenses = Expenses() let consumptionSubjectList = ["个人","企业"] var body: some View { NavigationView { List(self.expenses.consumptionList) { item in HStack { VStack { Text("\(item.title)") .bold() Text("\(self.consumptionSubjectList[item.consumptionSubject])") } Text("¥\(item.prise)") } } .navigationBarTitle(Text("消费记录")) .navigationBarItems(trailing: Button(action: {self.windowShow.toggle()}) { Image(systemName: "plus") }) } .sheet(isPresented: $windowShow){ AddRecording() } } }
struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
struct Action: Identifiable, Codable { var id = UUID() var --- title: String var consumptionSubject: Int var prise: Double }
class Expenses: ObservableObject { @Published var consumptionList: [Action]{ didSet { let encoder = JSONEncoder() if let encoded = try? encoder.encode(consumptionList){ UserDefaults.standard.set(encoded, forKey: "consumptionList") } } } init() { if let consumptionList = UserDefaults.standard.data(forKey: "consumptionList") { let decoder = JSONDecoder() if let decoded = try? decoder.decode([Action].self, from: consumptionList){ self.consumptionList = decoded return } } self.consumptionList = [] } }
|
AddRecording.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
import SwiftUI
struct AddRecording: View { @State var type = "" @State var consumptionSubject = 1 @State var prise = "" var body: some View { NavigationView { Form { TextField("消费名称", text: $type) Picker(selection: $consumptionSubject, label: Text("消费主体")) { Text("私人").tag(1) Text("公共").tag(2) } HStack { Text("¥") TextField("消费金额", text: $prise) .keyboardType(.decimalPad) } } .navigationBarTitle(Text("添加消费")) .navigationBarItems(trailing: Button(action: {}) { Text("保存") }) } } }
struct AddRecording_Previews: PreviewProvider { static var previews: some View { AddRecording() } }
|