#9.3點餐系統App
Published in
25 min readDec 1, 2022
程式重點:
- viewWillAppear
- 陣列indices,enumerated,比對陣列字串
- Cell的performBatchUpdates
- Button的convert來確認點擊的cell
- NavigationCotroller換頁傳值
- 商品根據點選,進行修改和新增
感想:這是專案當中,最多細節需要注意的地方.很茫然,也很崩潰.
以下是讓人崩潰的清單.
- 點選商品客製化選項後,進行換頁的判讀.
- 回傳頁資料的更新(包含客製化按鈕和內容)
- 判讀有無去回傳頁的狀況.
- 商品本身的點選
- 商品本身價錢的計算,以及客製化價錢的加總
- Cell本身重複利用的問題
讓我們開始Coding吧!
準備struct型別資料
依照製作Dynamic Prototypes的流程,製作出cell
//套餐選單的資料
struct ShopItemSet{
//商品名稱
let name:String
//商品價錢
let price:Int
//商品有無點選
var isadd = false
//商品類別,用來區分同個section,但不同商品類別的狀況
let setNumber:Int
//商品客製化選項
var choices: [String]
}
//section的分類
enum ShopItemSetSectionInt:Int{
case meal
case drink
case toppingGrade
}
class shopHambugerTableViewController: UITableViewController{
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let foodSection = ShopItemSetSection(rawValue: section)!
switch foodSection{
case.meal:
return "主餐"
case.drink:
return "套餐20元飲料"
case.toppingGrade:
return "加料升級優惠(現省5元)"
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 3
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if section == 0 {
return meals.count
}
else if section == 1 {
return drinks.count
}
else {
return toppingSets.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: ShopHambugerTableViewCell.reuseIdentifeir , for: indexPath) as! ShopHambugerTableViewCell
let food:ShopItemSet
//先設計為可點選
//isUserInteractionEnabled 不是所有的元件 都可以用.使用isEnabled會反灰,但使用這個不會,卻也不能點選
//帶有UI的元件都可以使用
cell.itemAdd.isUserInteractionEnabled = true
//指定位置
if indexPath.section == 0 {
food = meals[indexPath.row]
if indexPath.row == 0 {
// cell.itemAdd.isEnabled = false
cell.itemAdd.isUserInteractionEnabled = false
}
}
else if indexPath.section == 1 {
food = drinks[indexPath.row]
}
else{
food = toppingSets[indexPath.row]
}
cell.itemName.text = food.name
cell.itemPrice.text = String("+$\(food.price)")
cell.itemAdd.isSelected = food.isadd
cell.itemAdd.tag = indexPath.row
//跟上面處理換頁的手法一樣去處理cell的button
switch indexPath.section{
case 0:
let meal = meals[indexPath.row]
cell.itemCustomization.setTitle("\(meal.choices)", for: .normal)
case 1:
let drink = drinks[indexPath.row]
switch drink.setNumber{
case 1:
cell.itemCustomization.setTitle("\(drink.choices)", for: .normal)
case 2:
cell.itemCustomization.setTitle("\(drink.choices)", for: .normal)
default:
break
}
case 2:
let topping = toppingSets[indexPath.row]
cell.itemCustomization.setTitle("\(topping.choices)", for: .normal)
default:
break
}
return cell
}
}
如此一來,基本的表格資料可以被製作出來.
製作出點選Button的邏輯
這部分最重要的事情是把點選Button跟選定Cell的狀態綁在一起.因為點選的Button同時間代表Cell的內容.要進行內容的紀錄和更新.
@IBAction func buttonAdd(_ sender: UIButton) {
sender.isSelected.toggle()
resetDrinks()
let point = sender.convert(CGPoint.zero, to: tableView)
if let indexPath = tableView.indexPathForRow(at:point){
//ShopItemSetSectionInt設計成跟indexPath.section的型別一樣
let foodSection = ShopItemSetSectionInt(rawValue: indexPath.section)!
//裝入被點選的資料
var food:ShopItemSet
switch foodSection{
case.meal:
food = meals[indexPath.row]
case.drink:
for i in drinks.indices{
if drinks[i].isadd{
//如果其他有已經被點的話
sum -= drinks[i].price
var index1 = 0
for (j,indexMeal) in mealsAndtopping.enumerated(){
if indexMeal == drinks[i].name{
index1 = j
}
}
mealsAndtopping.remove(at: index1)
}
//如果其他有已經被點的話把它取消掉
drinks[i].isadd = false
}
//點選它,所以是true
drinks[indexPath.row].isadd = true
food = drinks[indexPath.row]
case.toppingGrade:
//toogle 點選可取消
//button的按鈕
if toppingSets[indexPath.row].isadd{
}
else{
for i in toppingSets.indices{
if toppingSets[i].isadd{
//判斷 點擊
sum -= toppingSets[i].price
var index1 = 0
for (j,indexMeal) in mealsAndtopping.enumerated(){
if indexMeal == toppingSets[i].name{
index1 = j
}
}
mealsAndtopping.remove(at: index1)
}
toppingSets[i].isadd = false
}
toppingSets[indexPath.row].isadd = true
}
//防止Cell本身重複利用的問題
food = toppingSets[indexPath.row]
}
if food.isadd{
//計算價錢
sum += food.price
//商品加入陣列
mealsAndtopping.append(food.name)
}
else{
sum -= food.price
//用商品名字搜尋商品陣列,並移除
var index = 0
for (i,orderitem) in mealsAndtopping.enumerated(){
if orderitem == food.name{
index = i
}
}
mealsAndtopping.remove(at: index)
}
print("mealsAndtopping", mealsAndtopping)
print(sum)
//同步更新howMuchButton的價錢和數量
howMuchButton.setTitle("加入購物車 +$\(sum*numberSum)", for: .normal)
}
tableView.reloadData()
}
- Cell的performBatchUpdates
點選Button後,Cell會展開,顯示出東西.
selectIndexPath會根據點選Button的Cell,來進行更動.把邏輯加入buttonAdd函式當中.
//先設置第一個indexPath來抓取cell的位置
var selectIndexPath = IndexPath(row: 0, section: 1)
var showItemDetailButton = false {
didSet {
//允許新增刪除等等的動畫化
tableView.performBatchUpdates(nil)
}
}
@IBAction func buttonAdd(_ sender: UIButton) {
sender.isSelected.toggle()
resetDrinks()
let point = sender.convert(CGPoint.zero, to: tableView)
if let indexPath = tableView.indexPathForRow(at:point){
let foodSection = ShopItemSetSectionInt(rawValue: indexPath.section)!
var food:ShopItemSet
switch foodSection{
case.meal:
food = meals[indexPath.row]
//把上面設置的indexPath 分別去等於 選擇的cell
selectIndexPath = indexPath
case.drink:
for i in drinks.indices{
if drinks[i].isadd{
sum -= drinks[i].price
var index1 = 0
for (j,indexMeal) in mealsAndtopping.enumerated(){
if indexMeal == drinks[i].name{
index1 = j
}
}
mealsAndtopping.remove(at: index1)
}
drinks[i].isadd = false
}
drinks[indexPath.row].isadd = true
//不是nil(代表是有進過客製化頁面),但選了後不必進去客製化的頁面,也可以更新單筆資料
if toppingsDrink != nil {
toppingsDrink.choices = drinks[indexPath.row].choices
}
//是true就展開
if selectIndexPath.row == 8 || selectIndexPath.row == 9 || selectIndexPath.row == 12 {
//不展開
showItemDetailButton = true
}else{
//現在被展開的
selectIndexPath = indexPath
showItemDetailButton = false
}
food = drinks[indexPath.row]
case.toppingGrade:
//toogle 點選可取消
//button的按鈕
if toppingSets[indexPath.row].isadd{
toppingSets[indexPath.row].isadd = false
//點選取消時,同時取消展開cell
showItemDetailButton = true
}
else{
for i in toppingSets.indices{
if toppingSets[i].isadd{
sum -= toppingSets[i].price
var index1 = 0
for (j,indexMeal) in mealsAndtopping.enumerated(){
if indexMeal == toppingSets[i].name{
index1 = j
}
}
mealsAndtopping.remove(at: index1)
}
toppingSets[i].isadd = false
}
toppingSets[indexPath.row].isadd = true
//指定的cell展開
selectIndexPath = indexPath
//cell展開
showItemDetailButton = false
}
food = toppingSets[indexPath.row]
}
if food.isadd{
sum += food.price
mealsAndtopping.append(food.name)
}
else{
sum -= food.price
var index = 0
for (i,orderitem) in mealsAndtopping.enumerated(){
if orderitem == food.name{
index = i
}
}
mealsAndtopping.remove(at: index)
//為空的話就有預設值
if mealsAndtopping.isEmpty{
mealsAndtopping.append("請選擇加料")
}
}
print("mealsAndtopping", mealsAndtopping)
print(sum)
//同步更新howMuchButton的價錢和數量
howMuchButton.setTitle("加入購物車 +$\(sum*numberSum)", for: .normal)
}
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath == selectIndexPath,
showItemDetailButton == false {
// 基本高度
return 92
}
//強制打開IndexPath(row: 0, section: 0)的高度
else if indexPath == IndexPath(row: 0, section: 0){
return 92
}
else {
return UITableView.automaticDimension
}
}
- 點選商品客製化Button後,進行換頁的判讀.
@IBAction func goToItemCustomization(_ sender: UIButton) {
//分類
self.drinkDic = Dictionary(grouping: drinks, by: { drink in
drink.setNumber
})
//點擊的按鈕
let point = sender.convert(CGPoint.zero, to: tableView)
if let indexPath = tableView.indexPathForRow(at:point){
//透過section 去分類,section本身就是Int,也因為如此,可以使用switch
switch indexPath.section{
case 0:
//用變數先把選到的indexPath.row裝起來,同時也代表選到的cell
//重要!不這樣做,會出現optional的空值
toppingsMeal = meals[indexPath.row]
//執行換頁
performSegue(withIdentifier: "HamburgerToppingTableViewController", sender: nil)
case 1:
let drink = drinks[indexPath.row]
//使用出數據中的Int,跟switch的屬性進行結合,這樣就能從同個section去區分出不同的cell
switch drink.setNumber{
case 1:
toppingsDrink = drinks[indexPath.row]
performSegue(withIdentifier: "HamburgerDrinkIceAndLargeTableViewController", sender: nil)
case 2:
toppingsDrink = drinks[indexPath.row]
performSegue(withIdentifier: "HamburgerDrinkIceTableViewController", sender: nil)
default:
break
}
case 2:
toppingsTopping = toppingSets[indexPath.row]
performSegue(withIdentifier: "胡椒", sender: nil)
default:
break
}
}
}
換頁的同時,進行傳值
//客製化東西的陣列
var toppingsAll:[String] = []
var toppingsMeal:ShopItemSet!
var toppingsDrink:ShopItemSet!
var toppingsTopping:ShopItemSet!
//IBSegueAction 傳值
@IBSegueAction func showHamburgerTopping(_ coder: NSCoder, sender: Any?, segueIdentifier: String?) -> HamburgerToppingTableViewController? {
let controller = HamburgerToppingTableViewController(coder: coder)
controller?.toppingsMeal = toppingsMeal
controller?.sum = sum
//不是nil,代表客製化選項有被點擊
if toppingsMealShopItemFood != nil{
controller?.toppings = toppingsMealShopItemFood
}
return controller
}
//IBSegueAction 傳值
@IBSegueAction func showHamburgerDrinkIceAndLarge(_ coder: NSCoder, sender: Any?, segueIdentifier: String?) -> HamburgerDrinkIceAndLargeTableViewController? {
let controller = HamburgerDrinkIceAndLargeTableViewController(coder: coder)
controller?.toppingsDrink = toppingsDrink
controller?.sum = sum
drinkSum = sum
if toppingsDrinkShopDrinkItem != nil{
controller?.mediumOrBigs = toppingsDrinkShopDrinkItem
}
print("有拿到大杯冰的飲料")
return controller
}
//IBSegueAction 傳值
@IBSegueAction func showHamburgerDrinkIce(_ coder: NSCoder, sender: UIButton, segueIdentifier: String?) -> HamburgerDrinkIceTableViewController? {
let controller = HamburgerDrinkIceTableViewController(coder: coder)
controller?.toppingsDrink = toppingsDrink
print("有成功傳冰的飲料")
return controller
}
//IBSegueAction 傳值
@IBSegueAction func showHamburgerPepper(_ coder: NSCoder, sender: Any?, segueIdentifier: String?) -> HamburgerPepperTableViewController? {
let controller = HamburgerPepperTableViewController(coder: coder)
controller?.toppingsTopping = toppingsTopping
return controller
}
下一頁回傳值,兩頁進行交叉傳值
@IBAction func checkTopping(_ sender: Any) {
guard let navigationCotroller = self.navigationController else { return }
let count = navigationCotroller.viewControllers.count
let gobackList = self.navigationController?.viewControllers[ count - 2] as! shopHambugerTableViewController
//傳值
gobackList.toppingsMeal = toppingsMeal
gobackList.toppingsMealShopItemFood = toppings
gobackList.sum = sum
navigationCotroller.popToViewController(gobackList, animated: true)
}
- 在viewWillAppear進行更新
//從別的頁面回來時能夠進行更新
override func viewWillAppear(_ animated: Bool) {
//找尋陣列當中哪個是true,因為點擊的必定是true
var index1 = 0
for i in toppingSets.indices{
if toppingSets[i].isadd{
index1 = i
print("ShopHambugerTableViewCell",index1)
}
}
//直接指定section 然後利用true去找出是第幾個row
let indexPath1 = IndexPath(row: index1, section: 2)
//利用不是nil就代表是第二次進入viewWillAppear.不加這樣第一次跑viewWill的時候就會閃退
if toppingsTopping != nil{
//重置按鈕上面的文字
toppingSets = [
ShopItemSet(name: "三角薯餅(3塊)(原價25元)", price: 20, setNumber: 4, choices: ["香料"]),
ShopItemSet(name: "麥克雞塊(4塊)(原價35元)", price: 30, setNumber: 4, choices: ["香料"]),
ShopItemSet(name: "美式雞柳條(原價45元)", price: 40, setNumber: 4, choices: ["香料"])]
//選定的東西回頭更新toppingSets的資料
toppingSets[indexPath1.row].choices = toppingsTopping.choices
toppingSets[indexPath1.row].isadd = toppingsTopping.isadd
print("選擇單一更新成功",toppingSets[indexPath1.row])
}
toppingsAll = []
//如果有值就加到toppingsAll的陣列裡面,分別確保不是nil值,代表的確有東西且不會洗掉.前面的陣列清空,確保不會有重複的東西出現
if let toppingsMeal {
toppingsAll += toppingsMeal.choices
print("toppingsAll(toppingsMeal)",toppingsMeal.choices)
}
if let toppingsDrink {
toppingsAll += toppingsDrink.choices
print("toppingsAll(toppingsDrink)",toppingsDrink.choices)
}
if let toppingsTopping{
toppingsAll += toppingsTopping.choices
print("toppingsAll(toppingsTopping)",toppingsTopping.choices)
}
print("toppingsAll",toppingsAll)
tableView.reloadData()
}
用prepare把單筆訂單傳到下一頁
//傳單筆訂單到下一頁
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "orderList" {
//傳值前再確認一次
toppingsAll = []
if let toppingsMeal {
toppingsAll += toppingsMeal.choices
}
if let toppingsDrink {
toppingsAll += toppingsDrink.choices
}
if let toppingsTopping{
toppingsAll += toppingsTopping.choices
}
let selectItem = orderMenu(itemName: shopSellNameStr, priceSume: sum*numberSum, toppingAndMeals: mealsAndtopping, textField: textFieledWritingHambuger, itemNumber: numberSum, toppingAll: toppingsAll)
let controller = segue.destination as! OrderListTableViewController
controller.orderListGet = selectItem
print("即將回傳的toppingsAll",toppingsAll)
}
}
參考資料: