This code is not specifically mine but it will show the problem I am having more clearly. It is a part of an expense calculation application. I am having a hard time manipulating data after transferring it between views. I have 2 views and I would like to take all of the expenses and add them up. Part of the problem is that I make a new instance of a class every time I add a new "expense".
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($0)
}
}
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()
}
})
}
}
}
//SecondView
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()
}
}
It really depends on where you want to show the total. The easiest solution is to display the total on the ContentView
, after the list of all the expenses.
But first we need to calculate the total. As the total relates to the our Expenses
ObservableObject
it makes sense that it calculates it is own total.
The Expenses
class only has one instance and it contains multiple instances of the ExpenseItem
struct. We are going to solve this by adding a computed property to the Expenses
class. I have removed some code from your examples so as to highlight the changes that I have made.
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
}
}
}
The computed property total reduces all the ExpenseItem
amounts into a single value. You can read more about reduce here , and you can read about computed properties here . We don't have to use a reduce, we could use a for-loop and sum the values. We don't have to use a computed property either, we could use a function. It really depends on what you want.
Next in the ContentView
we can add a view to the List
that shows the total.
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)")
}
}
// ...
}
// ...
}
This will give you a result that looks like this.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.