上一章中我們完成了菜單的畫面,本章會加入點餐的動作。我們會介紹如何使用 ActionSheet 這個 UI 元件。此外,還會介紹如何在 Swift 使用 Singleton 物件。這是常常使用的技巧。
SwiftUI Coffee Shop
加入 ActionSheet
點餐的動作是,點擊商品列,會顯示 ActionSheet,詢問是否要將此商品加入到帳單。畫面如下:

所以首先我們要加上 ActionSheet。回到 MenuRowView,新增下方的程式碼到 Button 。
對 Button 呼叫 .actionSheet(isPresetned:),如果 showingActionSheet 是 true 的話,就會顯示裡面的 ActionSheet。
ActionSheet 的第一個參數標題。第二參數是按鈕。第一個按鈕是可自訂動作的按鈕。第二個按鈕是預設的 Destructive 按鈕,它會顯示紅色的字,而且沒有任何動作。當你點擊任一個按鈕後,showingActionSheet 會自動被設為 false。
struct MenuRowView: View {
...
@State var showingActionSheet = false
var body: some View {
Button(action: {
self.showingActionSheet = true
}) {
...
}
...
.actionSheet(isPresented: self.$showingActionSheet) {
ActionSheet(title: Text(product.name ?? ""), buttons: [
.default(Text("Add")) {
OrderManager.shared.add(product: self.product)
},
.destructive(Text("Cancel")),
])
}
...
}
}訂單管理
剛剛的第一個按鈕中,我們會將商品加入一個叫 OrderManager 的物件中,他會紀錄所有被點的商品。
在 Model 資料夾下新增 OrderManager 和 Order,程式碼如下方。OrderManager 會管理數筆訂單。而 Order 代表一筆訂單,它紀錄著商品的資訊和數量。OrderManager 提供 add(product:) 方法可以加入商品。如果商品的訂單已經存在,則增加數量。
我們希望 OrderManager 在整個 App 裡面只有一個實體,以便於管理,這也叫做 Singleton。它是用來管理訂單,所以不需要多於一個實體。因此,我們宣告一個靜態的 Property 叫 shared。整個程式都會透過 shared 來存取這個唯一的 OrderManager。
class OrderManager {
static let shared = OrderManager()
var orders: [Order] = []
func add(product: Product) {
guard let id = product.id else {
return
}
if let index = orders.firstIndex(where: { $0.id == id }) {
orders[index] = orders[index].increased()
} else {
let order = Order(id: id, name: product.name ?? "", price: product.price, quantity: 1)
orders.append(order)
}
}
}struct Order {
let id: UUID
let name: String
let price: Double
var quantity: Int
func increased() -> Order {
var copy = self
copy.quantity += 1
return copy
}
}結語
SwiftUI 將 Button 整合了 ActionSheet,使它們變為更為好用。在 UIKit 中,這兩個元件是完全獨立的,所以在使用上也就沒有這麼地方便。另外,我們還介紹了 Singleton 的使用方式。我們常常會建立一些 class,而且希望整個 App 中只會有那一個實體。這時候我們就會用 Singleton 來實作。在 Swift 中,Singleton 的物件常常會用 shared、default、instance 等這幾個來作為靜態變數的名稱。









