建立第一個 SwiftUI App:Day 8 – Swipe to Delete

Photo by Toa Heftiba on Unsplash
Photo by Toa Heftiba on Unsplash
為了要讓商品列表的功能更家齊全,除了新增商品外,還要加上修改和刪除商品的功能。本章結束時,商品列表會包含一般列表基本的新增、刪除、編輯功能。此外,還有資料儲存和頁面導覽。幾乎所有 App 裡的列表都會包含這幾個功能。

為了要讓商品列表的功能更家齊全,除了新增商品外,還要加上修改和刪除商品的功能。本章結束時,商品列表會包含一般列表基本的新增、刪除、編輯功能。此外,還有資料儲存和頁面導覽。幾乎所有 App 裡的列表都會包含這幾個功能。

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 叫 productProductListView 會傳使用者點擊要修改的商品過來。每個 View 的 .onAppear(perform:) 會在即將被顯示時呼叫的方法。所以,在最上層 View 的 .onAppear(perform:) 中,我們把 product.nameproduct.price 設給 nameprice

當使用者點擊 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 元件,但是基本的使用原理卻是不變的。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

You May Also Like
Photo by Alex Alvarez on Unsplash
Read More

Dispatch Queue 教學

GCD 提供有效率的並行處理,讓我們不需要直接管理多執行緒。它的 Dispatch Queues 可以循序地(serially)或是並行地(concurrently)執行任務。我們只需要將要並行的程式當作任務提交到 dispatch queues 就可以了。
Read More
Photo by Florinel Gorgan on Unsplash
Read More

如何製作一個 XCFramework

XCFramework 讓你可以將 iPhone、iPhone 模擬器等多的不同平台的二進位碼打包到一個可發佈的 .xcframework 檔。你只需要為你的 Framework 產生出一個 .xcframework 檔,就可以支援多種平台。
Read More
Photo by Fabian Gieske on Unsplash
Read More

SwiftUI @State & @Binding 教學

SwiftUI 推出了兩個 Property Wrapper – @State and @Binding。利用它們可以達到變數的 Two-way Binding 功能。也就是當變數的值改變時,它會重新被顯示。本章藉由製作一個 Custom View 來展示如何使用 @State 和 @Binding。
Read More
Photo by Svitlana on Unsplash
Read More

iOS:禁止螢幕截圖

基於一些理由,我們可能會想要禁止使用者對我們的 app 做螢幕截圖。然而,iOS 並沒有提供這樣的功能。所幸,我們可以利用 UITextField 來達到此效果。
Read More