#23 訂飲料 APP — 天仁茗茶

這一個程式希望達到的功能有:

  1. APP 能連結到 Airtable 後台做資料交換。
  2. 首頁能展示不同類別的品項。
  3. 點下想訂購的商品可以跳到下一頁訂購的頁面。
  4. Tab bar 有跳選到預訂購的產品頁面。
  5. 點選預訂購的產品可以跳到修改的頁面。
  6. 滑動已選擇預訂購的產品可以刪除該產品。
  7. 購物車 Tab bar 的 item 能夠顯示已經訂購產品的數量。
  8. 能夠自動判別不同產品規格的切換 (比方說有的產品有中杯跟大杯,有些只有中杯)。
  9. 訂單能夠依照生成的時間排序。

因為製作 APP 的過程比較長,遇到一些有趣的事情:

  1. 發現天仁茗茶並不是每一家的產品品項都一樣。
  2. 其實我喝飲料不常加料,製作時參考學姊的內容,才赫然發現一堆不知道的好料,所以我把他們全部加到程式內了,也不知道天仁茗茶是不是有這麼多的料。
  3. 有的時候程式寫太久會需要點選 “Clean Build Folder”,不然你找到死都不知道哪裡寫錯了(感謝 Peter 的指導)。
  4. Autolayout 如果設定錯程式也會死 (我原以為只會看不見畫面)。
  5. 在 textField 的欄位裡,「單純地用鍵盤打入文字」跟「打入文字後按下 return」是完全不同的兩件事 (感謝 Peter 的指導)。

接下來就來介紹如何完成這個訂購天仁茗茶商品的 APP 吧!

註冊 Airtable

🍎 他會帶你選一些欄位,隨便選一選,然後都刪掉。

加了一堆阿里不達的欄位
把他們都刪掉然後 Name 改成 name

🍎 新增欄位並指定類型,比方說 image 的類型就是 attachment。(這裡指的是你建立的目錄 “menu”,你要上傳的 “order” 裡面的 imageUrl 類型變成 text)

🍎 連結到 airtable 的 API 網頁。

🍎 點選你建立好的 workspace,研究一下他的 JSON 結構。

🍎 然後他會叫你去 account 頁面拿鑰匙。

🍎 API 網址為紅框中的網址。並且裡面也告訴你,使用 API Key 的方法。

🍎 然後你需要生成一個鑰匙。

點擊 Generate API Key
點下去秒生出來

🍎 讀一下內容,有些地方要注意。

🍎 我一共建立 5 個 menu 跟 1 個 order。

🍎 重新整理 Airtable 就可以看到資料結構。

第一層
第二層

建立 Storyboard

設計 Main View

🍎 先在 sotryboard 中的 View Controller 建立三個 View:最上面是一個 View 內含一個 Image View,後面兩個是 Scroll View。

🍎 要建立塞進中間那個 Scroll View 的 View,先在外面處理好,再一口氣塞進去。

🍎 我放了 5 個 Button。

🍎 直接拖曳進去 Scroll View。

🍎 下面那個 Scroll View 先新增一個 Container View。

🍎 再新增一個 Collection View,取代那個 Container View。

🍎 先刪除外面那個連結的方框。

🍎 再連結 Container View 與 Collection View。選 Embed。

🍎 就完成連結了。

🍎 然後把相關的元件佈置在 Collection View Cell 中。因為我有 5 份 menu 所以做 5 次。Collection View Cell 可以用複製的。

設計 Order View

🍎 新增一個 View Controller,分成 3 等份:一個 View 是用來顯示產品圖片跟名稱的 (黃色底是設計時分辨用,之後會拿掉),第二個是 Table View,有 5 個 Table View Cell,是用來選產品內容的,第三個是用來計算跟顯示定產品的數量跟價錢的。相關的 Autolayout 請參考附件。

🍎 然後把每一個 Collection View Cell 都連結到這個 View Controller 上。

🍎 這樣就完成了,設計理念是當你在首頁 (Main View) 看到喜歡的飲料圖片時,點選他就會進入到訂購的次頁 (Order View)。

設計 Count View (已選購訂單頁面)以及 Edit View (修改訂單頁面)

🍎 這裡顯示相關的 View Controller 佈置。

🍍 加上 Tab Bar Controller 完成 storyboard 的部署。

程式部分

介紹佈局

🍎 程式分為 Model、View、Controller 三大部分。

🍎 Model 分為 Enums、Menu、Order、updateOrder 四個部分。

🍎 View 的部分就是 Storyboard 跟開機畫面 (這裡沒有使用)。

🍎 Controller 分為三個部分 Main、Support、Menu。

🍎 Main 的部分是四個 View Controller 的類別(Main、Order、Edit、Count)。

🍎 Support 的部分是在 Table View (Order View 與 Edit View) 上有 5 個 Table View Cell,所以建立相關的 swift。

🍎 Menu 的部分分為取得各資料的主檔案 MenuController 與 5 個 Menu 的Collection View 與 Collection View Cell 的 swift 檔。

🍎 接下來看看相關的程序與他的程式。擷取資料分為文字資料與圖片資料兩種。

擷取資料部分 (GET)

🍎 資料的定義 (Menu)

🍎 文字資料的擷取。

🍎 圖片資料的擷取。

🍎 在 MenuController 中建立 shared:

static let shared = MenuController()

🍎 這樣在 Collection View Controller 中就能把資料移轉過來。這邊多了一個固定 Collection View Cell 的方法,讓他排列更整齊。(以 menu1 為範例)

🍎 然後就是指定每個 Collection View Cell 的內容 (Main View 的展示飲品部分)。

🍎 記得處理有時候只有中杯沒有大杯的情況。如果沒有大杯,就在 Airtable 中以 0 取代。

public func priceIsZeroFormate(price: Int) -> String {
if price == 0 {
return "-"
} else {
return "\(price)"
}

🍎 這樣就完成 GET 的動作了。

訂購部分

🍎 資料的定義 (Order)。

🍎 從 Main View 的 Collection View 傳資料到下一頁 Order View 經由 Segue 需要設定在前一個 Swift 檔案中 (以 Menu1CollectionViewController.swift 為例)。

@IBSegueAction func showDrinkDetail(_ coder: NSCoder) -> OrderViewController? {
guard let item = collectionView.indexPathsForSelectedItems?.first?.item
else {return nil}
return OrderViewController.init(coder: coder, menuItem: menuRecords, indexPath: item)
}

🍎 Order View 接收前一頁的資料需要建立接收的變數常數,並且初始化。

🍎 設定 viewDidLoad,建立相關的 delegate 與 dataSource,確認 Table View 的選擇限制,並且設定好各標籤的表示方式。另外有抓取圖片、紀錄建立資料時間、以及更新訂單訂購的總價。

🍎 textFieldShouldReturn 配合 addToCartButtonClicked 的部分是確認訂單一定要有訂購人的名字,否則會跳出 showAlert 警告。如果沒問題,則利用 MenuController.shared.postOrder 訂單上傳。

🍎 plusButtonClicked 與 minusButtonClicked 是讓使用者可以增減訂購的數量,限制上限至 99 杯,不讓位數太高而顯示出問題,況且訂超過 100 杯商家準備也會有困難。

🍎 Table View 的部分我挑重點講。在設定 cellForRowAt 的地方,因為訂單裡面會出現只有中杯沒有大杯的情形,所以在 switch 的 capacity 部分需要判定 if-else。Topping 的部分按下 addToppingBtn 就會改圖案。然後每一個 cell 記得都要 delegate 還有更新狀態 updateDrinkStatus()。

🍎 在 didSelectRowAt 的地方,因為需要轉換成 String 以利上傳,所以比較複雜。然後這個地方因為要跟隨所選的項目改變狀態與總價,所以要updateDrinkStatus() 與 updateSubtotal()。最後不要忘記 tableView.reloadData()。

🍎 最後是使用 extension 的方式,把 CapacityTableViewCellDelegate、SugarTableViewCellDelegate、TempTableViewCellDelegate 給傳進來,他們的 protocol 都寫在各自的 CapacityTableViewCell.swift、SugarTableViewCell.swift、TempTableViewCell.swift 中。

上傳資料 (POST)

🍎 接下來建立已選預訂購的頁面 CountViewController.swift。

🍎 這邊希望 Tab bar 會顯示訂單的數量,所以建立 initTabItem () 的方法,主要是利用 badgeValue 的屬性顯示數量。那麼我們這裡需要可以刪除的功能,所以利用了所以利用了table view (commit) 配合 deleteOrder() 進行刪除。

CountViewController.swift
MenuController.swift

🍎 因為 CountViewController 是可以將資料傳送到 EditViewController,所以 Segue 必須建立在這一頁。

@IBSegueAction func showEditOrder(_ coder: NSCoder) -> EditViewController? {
guard let row = countTableView.indexPathsForSelectedRows?.first?.row else {return nil}
let vc = EditViewController(coder: coder, orderList: orderList, orderID: orderID, indexPath: row)
vc?.delegate = self
return vc
}

修改資料

🍎 資料的定義 (UpdateOrder)。

🍎 初始化資料的參數。

🍎 其他部分與 OrderViewController.swift 非常相似。

🍍 完成作品

參考資料

Github

--

--