Этот код не является моим, но он более четко покажет мою проблему. Это часть приложения для расчета расходов. Мне трудно манипулировать данными после их передачи между представлениями. У меня 2 просмотра, и я хотел бы взять на себя все расходы и сложить их. Отчасти проблема в том, что я создаю новый экземпляр класса каждый раз, когда добавляю новые «расходы».

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()
    }
}
0
Neil McGrogan 13 Июн 2020 в 05:25

1 ответ

Лучший ответ

Это действительно зависит от того, где вы хотите показать общую сумму. Самое простое решение - отобразить общую сумму в ContentView после списка всех расходов.

Но сначала нам нужно посчитать сумму. Поскольку общая сумма относится к нашему Expenses ObservableObject, имеет смысл вычислить ее как собственную сумму.

Класс Expenses имеет только один экземпляр и содержит несколько экземпляров структуры ExpenseItem. Мы собираемся решить эту проблему, добавив вычисляемое свойство к классу Expenses. Я удалил код из ваших примеров, чтобы выделить внесенные мной изменения.

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 здесь, и вы можете прочитать о вычисленных свойствах здесь. Нам не нужно использовать сокращение, мы могли бы использовать цикл 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)")
                }
            }
            // ...
    }
     // ...
}

Это даст вам результат, который выглядит примерно так.

Image showing the iExpense app with a total

0
Andrew 13 Июн 2020 в 05:00