这是一个记录消费的一个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
//
// ContentView.swift
// Practise2-7
//
// Created by 张洪Hoo on 2020/4/7.
// Copyright © 2020 张洪Hoo. All rights reserved.
//

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 = []
}
}

1403

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
//
// AddRecording.swift
// Practise2-7
//
// Created by 张洪Hoo on 2020/4/7.
// Copyright © 2020 张洪Hoo. All rights reserved.
//

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: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/) {
Text("保存")
})
}


}
}

struct AddRecording_Previews: PreviewProvider {
static var previews: some View {
AddRecording()
}
}

1403