100 Days of SwiftUI(DAY 37(Project7 part 2 )

削除できるリストを作成する
Building a list we can delete from

import SwiftUI
ファイル名;iExpense

struct ContentView: View {
    struct ExpenseItem {
        let name: String
        let type: String
        let amount: Int
    }

    class Expenses: ObservableObject {
        @Published var items = [ExpenseItem]()
    }
    
    @ObservedObject var expenses = Expenses()
    
    func removeItems(at offsets: IndexSet) {
        expenses.items.remove(atOffsets: offsets)
    }
    
    var body: some View {
        NavigationView {
            List {
                ForEach(expenses.items, id: \.name) { item in
                    Text(item.name)
                }
                .onDelete(perform: removeItems)
            }
            .navigationBarTitle("iExpense")
            .navigationBarItems(trailing:
                Button(action: {
                    let expense = ExpenseItem(name: "Test", type: "Personal", amount: 5)
                    self.expenses.items.append(expense)
                }) {
                    Image(systemName: "plus")
                }
            )
        }
    }
}

SwiftUIWorking with Identifiable items in SwiftUI
識別可能なアイテムを操作する
UUIDは「普遍的に一意の識別子
UUIDは、08B15DB4-2F02-4AB8-A965-67A9C90D8A44のような長い16進文字列です。したがって、これは8桁、4桁、4桁、4桁、12桁の数字であり、3番目のブロックの最初の数字に4があることが唯一の要件です。固定の4を減算すると、31桁になり、それぞれが16の値のいずれかになります。
Identifiable これはSwiftに組み込まれているプロトコルの1つであり、「このタイプは一意に識別できる」ことを意味します。

import SwiftUI

//------------------------------
struct ExpenseItem: Identifiable {
    let id = UUID()
    let name: String
    let type: String
    let amount: Int
}

//------------------------------
class Expenses: ObservableObject {
    @Published var items = [ExpenseItem]()
}

//------------------------------
struct ContentView: View {
    
    @State private var showingAddExpense = false

    @ObservedObject var expenses = Expenses()
    
    func removeItems(at offsets: IndexSet) {
        expenses.items.remove(atOffsets: offsets)
    }
    
    var body: some View {
        NavigationView {
            List {
                ForEach(expenses.items) { item in
                    Text(item.name)
                }
                .onDelete(perform: removeItems)
            }
            .navigationBarTitle("iExpense")
            .navigationBarItems(trailing:
                Button(action: {
                    let expense = ExpenseItem(name: "Test", type: "Personal", amount: 5)
                                       self.expenses.items.append(expense)
                }) {
                    Image(systemName: "plus")
                }
            )
//            .sheet(isPresented: $showingAddExpense) {
//                // show an AddView here
//                AddView(expenses: self.expenses)
//            }
        }
    }
}
//------------------------------

監視対象オブジェクトを新しいビューと共有する
Sharing an observed object with a new view
準拠するクラスはObservableObject、複数のSwiftUIビューで使用できます。これらのビューはすべて、クラスの公開されたプロパティが変更されると更新されます。
ファイル名;AddView.swift

struct AddView: View {
    
    @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")
        }
    }
}

struct AddView_Previews: PreviewProvider {
    static var previews: some View {
        AddView(expenses: Expenses())
    }
}

//--------------------------------------------------

struct ExpenseItem: Identifiable {
    let id = UUID()
    let name: String
    let type: String
    let amount: Int
}

class Expenses: ObservableObject {
    @Published var items = [ExpenseItem]()
}

struct ContentView: View {
    
    @State private var showingAddExpense = false

    @ObservedObject var expenses = Expenses()
    
    func removeItems(at offsets: IndexSet) {
        expenses.items.remove(atOffsets: offsets)
    }
    
    var body: some View {
        NavigationView {
            List {
                ForEach(expenses.items) { item in
                    Text(item.name)
                }
                .onDelete(perform: removeItems)
            }
            .navigationBarTitle("iExpense")
            .navigationBarItems(trailing:
                Button(action: {
                    self.showingAddExpense = true
                }) {
                    Image(systemName: "plus")
                }
            )
            .sheet(isPresented: $showingAddExpense) {
                AddView(expenses: self.expenses)
            }
        }
    }
}


+ボタンをタップAddViewして、さまざまなフィールドに入力できる場所を表示し、スワイプして閉じることができます。

UserDefaultsで変更を永続的にする
Making changes permanent with UserDefaults

次に、presentationMode.wrappedValue.dismiss()ビューにそれ自体を閉じさせたいときに呼び出す必要があります。これにより、showingAddExpensein のブール値がContentViewfalseに戻り、が非表示になりAddViewます。AddView新しい経費項目を作成して既存の経費に追加する[保存]ボタンが既にあるので、次の直後の行にこれを追加します。
isPresentedシートのパラメーターにリンク

self.presentationMode.wrappedValue.dismiss()


struct AddView: View {
    
    @ObservedObject var expenses: Expenses
    
    //追加
    @Environment(\.presentationMode) var presentationMode
    
    @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)
                }
                //追加 isPresentedシートのパラメーターにリンク
                self.presentationMode.wrappedValue.dismiss()
            })
        }
    }
}

struct AddView_Previews: PreviewProvider {
    static var previews: some View {
        AddView(expenses: Expenses())
    }
}

//--------------------------------------------------
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]()
    @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
            }
        }

        self.items = []
    }
}
//---------------------------------------
struct ContentView: View {
    
    @State private var showingAddExpense = false

    @ObservedObject var expenses = Expenses()
    
    func removeItems(at offsets: IndexSet) {
        expenses.items.remove(atOffsets: offsets)
    }
    
    var body: some View {
        NavigationView {
            List {
                ForEach(expenses.items) { item in
                    HStack {
                        VStack(alignment: .leading) {
                            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)
            }
        }
    }
}