iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 19
2
Software Development

無中生有-從SWIFT語法學習到iOS APP的開發系列 第 19

Day19 - bug解除 (自定義 delegate 傳值:protocol應用)

前言

困擾了快一個禮拜的 table view cell 傳值總算找到解法...一開始以為用 UserDefaults 的方法就可以解決,但是 UserDeafaults 的方法無法紀錄是哪個 cell 點選,所以最後還是只能選擇 delegate 的方式傳值。

網路上提供傳值的方法有很多種,大家可以參考下方資料

傳值方法

1.實作一個 tab bar controller

大家請參考我的文章 Day14 - 標籤列控制器 步驟1~4,新增一個含有三個標籤頁的 tab bar controller
效果如下

2.將 item 1 頁面 設定為 table view

最上方是一個 Navigation Bar,中間是 Table View,最下面當然是 Tab Bar

Navigaion Bar 和 Table View 裡面各有一些元件要另外拉

3.新增一個繼承 UIViewController 的類別

檔案命名為 drinkViewController,裡面要遵守 UITableViewDelegate、UITableViewDataSource原則

class drinkViewController: UIViewController, UITableViewDataSource
{

//... 程式碼略

}

table view code 設定如下
ps: cell identifier 命名為 "drinkCell"

    let teaList = ["茉莉綠茶", "阿薩姆紅茶", "四季春茶", "黃金烏龍", "微檸檬 紅/青", "檸檬 綠/青", "梅果綠", "8冰綠", "養樂多綠", "蜂蜜綠", "芒果青", "冰淇淋紅茶", "鮮柚綠", "波霸 紅/綠/青/烏", "波霸奶茶(大顆)", "波霸奶綠(大顆)", "珍珠 紅/綠/青/烏", "珍珠奶茶(小顆)", "珍珠奶綠(小顆)", "椰果奶茶", "仙草奶凍", "鮮柚汁(季節限定)"]

// 設定幾個 section
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

// 設定每個 section 幾個 cell
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return teaList.count
    }

// 設定 cell顯示內容
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        // ... 程式碼略
        
        return cell
    }

在 storyboard 中,令 Item 1 要符合 drinkViewController的類別

5.新增一個繼承 UITableViewCell 的類別

檔案命名為 DrinkTableViewCell

在 class DrinkTableViewCell ,新增一個 protocol,內含一個方法 "buttonTapped"

protocol DrinkTableViewCellDelegate: class {
    func buttonTapped(cell: DrinkTableViewCell, teaCount: Int)
}

在class DrinkTableViewCell內加入元件連結,並覆寫prepareForReuse()方法

class DrinkTableViewCell: UITableViewCell {
    var teaCount: Int = 0
    var delegate: DrinkTableViewCellDelegate?
    
    @IBOutlet weak var teaNameLabel: UILabel!
    @IBOutlet weak var teaCountLabel: UILabel!
    @IBAction func teaCountStepper(_ sender: UIStepper) {
        teaCount = Int(sender.value)
        //teaCountLabel.text = teaCount  如果購買數字寫在這,就會出現 Day18 所說的bug
        
        // 呼叫代理,並由 DrinkTableView.swift中實現 (實作方法下一步驟才寫)
self.delegate?.buttonTapped(cell: self, teaCount: teaCount)
    }

    // 覆寫 prepareForReuse()方法
    override func prepareForReuse() {
        super.prepareForReuse()
        self.delegate = nil
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }
}

在 storyboard 中,令 Item 1 中的 Table View 裡的 Table View Cell 要符合 DrinkTableViewCell 的類別

6. 回到 drinkViewController 中,實現 DrinkTableViewCellDelegate

令 class drinkViewController 額外遵守 DrinkTableViewCellDelegate 的協議

class drinkViewController: UIViewController, UITableViewDataSource, UITableViewDelegate ,  DrinkTableViewCellDelegate
{

// ...程式碼略

}

將 table view 元件連結進 class drinkViewController,並實現協議 DrinkTableViewCellDelegate 的 function

@IBOutlet weak var teaTableView: UITableView!

class drinkViewController: UIViewController, UITableViewDataSource, UITableViewDelegate ,  DrinkTableViewCellDelegate
{

// ...部分程式碼略

var selectTeaCount: [Int] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    func buttonTapped(cell: DrinkTableViewCell, teaCount: Int){
    
        // 就是這一步驟,讓程式可以紀錄是哪個 cell 的按鈕被點了
        guard let indexPath = self.teaTableView.indexPath(for: cell) else {
            // Note, this shouldn't happen - how did the user tap on a button that wasn't on screen?
            return
        }
        selectTeaCount[indexPath.row] = teaCount
        
        // 這邊 reloadData 是因為希望使用者按下 cell 裡面的 stepper 後,數字能及時更新到 cell 裡的 teaCountLabel
        teaTableView.reloadData()
        
    }

cell呈現內容,將 func tableView(cellForRowAt indexPath:)改寫

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "drinkCell", for: indexPath) as! DrinkTableViewCell
        
        cell.teaNameLabel?.text = teaList[indexPath.row]
        cell.teaCountLabel?.text = "\(selectTeaCount[indexPath.row])"
        
        // 這裡的 self 是指 drinkViewController了
        cell.delegate = self
        return cell
    }

7. 完成傳值,效果如下

後記

困擾了好幾天的 bug 總算解決,明天再來把儲值的陣列整理整理,並傳到購物車頁面呈現


上一篇
Day18 - 意外的小插曲
下一篇
Day20 - 儲存訂單 (segue傳值)
系列文
無中生有-從SWIFT語法學習到iOS APP的開發30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言