斯威夫特用户界面。我如何 access/manipulate 视图之间的数据
SwiftUI. How do I access/manipulate data between views
这段代码不是我的,但它会更清楚地显示我遇到的问题。它是费用计算应用程序的一部分。在视图之间传输数据后,我很难处理数据。我有 2 个意见,我想把所有的费用都加起来。部分问题是我每次添加新的 "expense".
时都会创建一个 class 的新实例
import SwiftUI
struct AddView: View {
@Environment(\.presentationMode) var presentationMode
@ObservedObject var expenses: Expenses
@State private var name = ""
@State private var type = "Personal"
@State private var amount = ""
static let types = ["Business", "Personal"]
var body: some View {
NavigationView {
Form {
TextField("Name", text: $name)
Picker("Type", selection: $type) {
ForEach(Self.types, id: \.self) {
Text([=11=])
}
}
TextField("Amount", text: $amount)
.keyboardType(.numberPad)
}
.navigationBarTitle("Add new expense")
.navigationBarItems(trailing: Button("Save") {
if let actualAmount = Int(self.amount) {
let item = ExpenseItem(name: self.name, type: self.type, amount: actualAmount)
self.expenses.items.append(item)
self.presentationMode
.wrappedValue.dismiss()
}
})
}
}
}
//第二视图
import SwiftUI
struct ExpenseItem: Identifiable, Codable {
let id = UUID()
let name: String
let type: String
let amount: Int
}
class Expenses: ObservableObject {
@Published var items = [ExpenseItem]() {
didSet {
let encoder = JSONEncoder()
if let encoded = try?
encoder.encode(items) {
UserDefaults.standard.set(encoded, forKey: "Items")
}
}
}
init() {
if let items = UserDefaults.standard.data(forKey: "Items") {
let decoder = JSONDecoder()
if let decoded = try?
decoder.decode([ExpenseItem].self, from: items) {
self.items = decoded
return
}
}
}
}
struct ContentView: View {
@ObservedObject var expenses = Expenses()
@State private var showingAddExpense = false
var body: some View {
NavigationView {
List {
ForEach(expenses.items) { item in
HStack {
VStack {
Text(item.name)
.font(.headline)
Text(item.type)
}
Spacer()
Text("$\(item.amount)")
}
}
.onDelete(perform: removeItems)
}
.navigationBarTitle("iExpense")
.navigationBarItems(trailing: Button(action: {
self.showingAddExpense = true
}) {
Image(systemName: "plus")
}
)
.sheet(isPresented: $showingAddExpense) {
AddView(expenses: self.expenses)
}
}
}
func removeItems(at offsets: IndexSet) {
expenses.items.remove(atOffsets: offsets)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
这实际上取决于您要显示总数的位置。最简单的解决方案是在所有费用列表之后的 ContentView
上显示总计。
但首先我们需要计算总数。由于总数与我们的 Expenses
ObservableObject
相关,因此计算它是自己的总数是有道理的。
Expenses
class 只有一个实例,它包含 多个实例 ExpenseItem
结构。我们将通过将计算的 属性 添加到 Expenses
class 来解决这个问题。我从您的示例中删除了一些代码,以突出显示我所做的更改。
class Expenses: ObservableObject {
@Published var items: [ExpenseItem]
// ...
// Computed property that calculates the total amount
var total: Int {
self.items.reduce(0) { result, item -> Int in
result + item.amount
}
}
}
计算得出的 属性 总数将所有 ExpenseItem
金额减少为一个值。您可以阅读更多关于 reduce here, and you can read about computed properties here 的内容。我们不必使用 reduce,我们可以使用 for 循环并对值求和。我们也不必使用计算 属性 ,我们可以使用函数。这真的取决于你想要什么。
接下来,在 ContentView
中,我们可以向 List
添加一个显示总数的视图。
struct ContentView: View {
@ObservedObject var expenses = Expenses()
@State private var showingAddExpense = false
var body: some View {
NavigationView {
List {
ForEach(expenses.items) { item in
// ...
}
.onDelete(perform: removeItems)
// View that shows the total amount of the expenses
HStack {
Text("Total")
Spacer()
Text("\(expenses.total)")
}
}
// ...
}
// ...
}
这会给你一个看起来像这样的结果。
这段代码不是我的,但它会更清楚地显示我遇到的问题。它是费用计算应用程序的一部分。在视图之间传输数据后,我很难处理数据。我有 2 个意见,我想把所有的费用都加起来。部分问题是我每次添加新的 "expense".
时都会创建一个 class 的新实例import SwiftUI
struct AddView: View {
@Environment(\.presentationMode) var presentationMode
@ObservedObject var expenses: Expenses
@State private var name = ""
@State private var type = "Personal"
@State private var amount = ""
static let types = ["Business", "Personal"]
var body: some View {
NavigationView {
Form {
TextField("Name", text: $name)
Picker("Type", selection: $type) {
ForEach(Self.types, id: \.self) {
Text([=11=])
}
}
TextField("Amount", text: $amount)
.keyboardType(.numberPad)
}
.navigationBarTitle("Add new expense")
.navigationBarItems(trailing: Button("Save") {
if let actualAmount = Int(self.amount) {
let item = ExpenseItem(name: self.name, type: self.type, amount: actualAmount)
self.expenses.items.append(item)
self.presentationMode
.wrappedValue.dismiss()
}
})
}
}
}
//第二视图
import SwiftUI
struct ExpenseItem: Identifiable, Codable {
let id = UUID()
let name: String
let type: String
let amount: Int
}
class Expenses: ObservableObject {
@Published var items = [ExpenseItem]() {
didSet {
let encoder = JSONEncoder()
if let encoded = try?
encoder.encode(items) {
UserDefaults.standard.set(encoded, forKey: "Items")
}
}
}
init() {
if let items = UserDefaults.standard.data(forKey: "Items") {
let decoder = JSONDecoder()
if let decoded = try?
decoder.decode([ExpenseItem].self, from: items) {
self.items = decoded
return
}
}
}
}
struct ContentView: View {
@ObservedObject var expenses = Expenses()
@State private var showingAddExpense = false
var body: some View {
NavigationView {
List {
ForEach(expenses.items) { item in
HStack {
VStack {
Text(item.name)
.font(.headline)
Text(item.type)
}
Spacer()
Text("$\(item.amount)")
}
}
.onDelete(perform: removeItems)
}
.navigationBarTitle("iExpense")
.navigationBarItems(trailing: Button(action: {
self.showingAddExpense = true
}) {
Image(systemName: "plus")
}
)
.sheet(isPresented: $showingAddExpense) {
AddView(expenses: self.expenses)
}
}
}
func removeItems(at offsets: IndexSet) {
expenses.items.remove(atOffsets: offsets)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
这实际上取决于您要显示总数的位置。最简单的解决方案是在所有费用列表之后的 ContentView
上显示总计。
但首先我们需要计算总数。由于总数与我们的 Expenses
ObservableObject
相关,因此计算它是自己的总数是有道理的。
Expenses
class 只有一个实例,它包含 多个实例 ExpenseItem
结构。我们将通过将计算的 属性 添加到 Expenses
class 来解决这个问题。我从您的示例中删除了一些代码,以突出显示我所做的更改。
class Expenses: ObservableObject {
@Published var items: [ExpenseItem]
// ...
// Computed property that calculates the total amount
var total: Int {
self.items.reduce(0) { result, item -> Int in
result + item.amount
}
}
}
计算得出的 属性 总数将所有 ExpenseItem
金额减少为一个值。您可以阅读更多关于 reduce here, and you can read about computed properties here 的内容。我们不必使用 reduce,我们可以使用 for 循环并对值求和。我们也不必使用计算 属性 ,我们可以使用函数。这真的取决于你想要什么。
接下来,在 ContentView
中,我们可以向 List
添加一个显示总数的视图。
struct ContentView: View {
@ObservedObject var expenses = Expenses()
@State private var showingAddExpense = false
var body: some View {
NavigationView {
List {
ForEach(expenses.items) { item in
// ...
}
.onDelete(perform: removeItems)
// View that shows the total amount of the expenses
HStack {
Text("Total")
Spacer()
Text("\(expenses.total)")
}
}
// ...
}
// ...
}
这会给你一个看起来像这样的结果。