為了要讓商品列表的功能更家齊全,除了新增商品外,還要加上修改和刪除商品的功能。本章結束時,商品列表會包含一般列表基本的新增、刪除、編輯功能。此外,還有資料儲存和頁面導覽。幾乎所有 App 裡的列表都會包含這幾個功能。
SwiftUI Coffee Shop
修改商品
修改商品頁面和新增商品頁面幾乎是一模一樣。主要差別在於,它是修改既有的商品。
在 Product 資料夾下,新增 EditProductView.swift 檔案。你可以複製 AddProductView
的程式碼,貼到 EditProductView
,因為 Layout 是一樣的。然後修改成以下的程式碼。其中省略的程式碼和 AddProductView
中的是一模一樣。我們這邊只顯示不一樣的部分。
struct EditProductView: View { @Environment(\.presentationMode) var presentationMode let product: Product ... var body: some View { VStack { ... Button(action: { let isSuccessful = self.editProduct() self.isFailed = !isSuccessful if isSuccessful { self.presentationMode.wrappedValue.dismiss() } }, label: { Text("Edit") }) ... } .navigationBarTitle("Edit a product") .onAppear(perform: onAppear) } private func onAppear() { name = product.name ?? "" price = product.price < 0 ? "" : String(product.price) } private func editProduct() -> Bool { guard !name.isEmpty else { return false } guard let parsedPrice = Double(price) else { return false } product.id = UUID() product.name = name product.price = parsedPrice do { try product.managedObjectContext?.save() return true } catch { print("\(error)") return false } } }
在 EditProductView
裡,我們新增了一個 Property 叫 product
。ProductListView
會傳使用者點擊要修改的商品過來。每個 View 的 .onAppear(perform:)
會在即將被顯示時呼叫的方法。所以,在最上層 View 的 .onAppear(perform:)
中,我們把 product.name
和 product.price
設給 name
和 price
。
當使用者點擊 Edit Button 時,editProduct(
) 會被呼叫。它和 AddProductView.addProduct()
很像,但 NSManagedObjectContext
的取得方式不一樣。
class Product
是繼承 NSManagedObject
。所以當 Product 被讀出來時,Product 便會紀錄當時的 Context。
// Product+CoreDataClass.swift @objc(Product) public class Product: NSManagedObject
所以最安全的方式是,用當初讀出這個 Product 的 NSManagedObjectContext
,來儲存對這個 Product 的修改。如果程式中有多個 NSManagedObjectContext
時,假如 Product 是用 Context A 讀取,但是用 Context B 儲存時,有可能會造成程式的崩潰 (Crash)。
try product.managedObjectContext?.save()
刪除商品
現在我們來加入刪除商品功能。我們希望當使用者在某個商品列上,向左滑動 (Swipe to left) 就會出現刪除的按鈕,這也就是滑動刪除 (Swipe to delete)。List
已經支援向左滑動手勢。我們只要在 ForEach()
下呼叫 .onDelete(perform:)
,就可以了。
因為 .onDelete(perform:)
會傳送被滑動的商品索引值 (Index),所以在 delete(at:)
中,利用這個索引值找出在 products
中的 product
。然後,呼叫 context.delete()
刪除,記得之後還要呼叫 context.save()
。不然它不會真的把該 product
從 Storage 中刪除。
struct ProductListView: View { ... var body: some View { List { ForEach(products, id: \.id) { product in ... } .onDelete(perform: delete) } ... } private func delete(at offsets: IndexSet) { for index in offsets { context.delete(products[index]) } do { try context.save() } catch { print("\(error)") } } }
結語
我們終於完成商品列表的基本功能。不知是否有覺得這個章節相對簡單呢?如果有,那表示你已經對 UI 元件和 Core Data 的使用很熟悉了。如果沒有,那請先別進入到下個章節。先回頭好好熟悉之前的章節會比較好。之後的章節會介紹更多 UI 元件,但是基本的使用原理卻是不變的。