#37 在 TableView Cell 裡使用 button 顯示下一頁,並傳資料到下一頁
練習動態 TableView + UIButton + 利用轉換座標的 convert function 判斷點選的 button 在哪個 cell
最近在練習訂飲料 App 的時候,想做類似像大苑子的訂購系統,可以從飲料列表按+button之後進入到下一頁,並傳資料到下一頁的功能。
這個功能如果TableView 是利用Static cells 來做的話,就比較簡單。但是如果是Dynamic Prototypes 來做的話,就必須讓 App 知道我現在按的 button 是在那個 Cell,這樣才能傳正確的資料到下一頁。
我們就來一步一步學習如何實現這個功能。
事先準備
- 首先,建立一個新的專案,命名為 UIbutton at TableView Cell
2. 因為等一下我們直接在 TableView Controller直接建立程式,所以我們切換到 Main StoryBoard,將 ViewController 跟 ViewController.swift 刪除
ViewController.swift 刪除時選取 Move to Trash就可以了
刪除完如下圖
3. 在 Main StoryBoard 加入 TableView Controller
然後在is initial View Controller 打勾,讓 App 打開一進來就直接讀取這個 App。
4. 建立一個新的TableViewController.swift 檔案,等一下將這個檔案聯結到在 Main StoryBoard 的 TableView Controller 的 Class ,這樣我們就可以讓TableView Controller 做我們想讓他做的事情。
按 command + N,選 Cocoa Touch Class 然後按 Next
將 Subclass of 選擇 UITableViewController,Class 命名為MenuTableViewController,然後按 Next
再來就是 Targets 打勾,然後按Create
這樣,MenuTableViewController.swift就建立完成了
5. 再來就是將 TableViewController 的 Class 指定為 MenuTableViewController.swift
點選 Main StoryBoard,點選 Table View Controller,點選show the identity inspector,將 Class 指定為MenuTableViewController,這樣子,我們就可以對 TableView Controller 為所欲為了。
6. 再來,我們來自定義 TableView Cell 裡的內容。
先按+(Library)選擇 Label, 將 label 拉到 Cell 的左邊。
將 Label 的 width 調到 200
同樣的方法將 button 加到 Cell 的右邊
7. 我們也要將 Cell 新增一個 TableViewCell.swift 檔案,將 cell 的 Class 指定至新的 TableViewCell.swift
一樣按 command + N,選 Cocoa Touch Class,將 Subclass of 選擇 UITableViewCell,Class 命名為MenuTableViewCell
然後將 Cell 的 Class 指定為 MenuTableViewCell
8. 要將 Cell 的內容顯示出來,必須將 cell 註明一個ID,這樣等一下程式才知道是要使用哪一個 Cell
就把 剛剛自定義的Cell 的ID 取名為MenuTableViewCell吧~
9. 最後的準備工作就是將目前自定義 cell 裡的 button 跟 Label 拉 IBOutlet
按住 Control + Option + command 然後按 enter 將Assiatant 視窗叫出來
然後點選 MenutableViewCell 底下的 Content View 然後選擇 MenuTableViewCell.swift
將 Label 拉 IBOutlet 並命名為 drinkLabel,將 button 拉 IBOutlet 並命名為 addButton
10. 最後, 將 Button 的 title 刪除,然後 Image 選擇 Plus,這樣 UIButton 就會顯示+的符號
以上,事前工作準備完成。
開始寫程式讓飲料名稱顯示在 tableview cell 上
點選 MenuTableViewController.swift,我們會用到的 TableView data source 的 function只有
- func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
- override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
所以其他沒有必要的程式碼就可以刪除了。結果如下圖
再來我們建立飲料菜單的 Array,menus[]
let menus = ["春熙柳橙翡翠","愛文芒果冰沙","愛文翡翠","番茄梅","芭樂梅"]
然後在tableView 的 numberOfRowsInSection function 顯示 menus 的 cell 數量
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {return menus.count}
再來,在tableView 的 cellForRowAt function 顯示 cell 內容
有幾個關鍵點要輸入
- withIdentifier 的字串要輸入我們剛剛自定義的Cell 的ID 也就是MenuTableViewCell
- cell 要轉型成我們自己建立的MenuTableViewCell,這樣才可以讀取到 MenuTableViewCell裡的東西
這樣就會變成
let cell = tableView.dequeueReusableCell(withIdentifier: "MenuTableViewCell", for: indexPath) as! MenuTableViewCell
在tableView 的 cellForRowAt function 裡有一個參數 為 indexpath,這個參數就是讓cell的每一行可以依照 menus 裡有多少種飲料來決定顯示的內容。
所以我們就可以定義menu
let menu = menus[indexPath.row]
意思就是cell 的第一行 就是 indexpath.row = 0
這樣 menus[0] 就是”春熙柳橙翡翠”
然後 cell 的 drinkLabel 的文字就是 menu
完整程式碼如下
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {let cell = tableView.dequeueReusableCell(withIdentifier: "MenuTableViewCell", for: indexPath) as! MenuTableViewCelllet menu = menus[indexPath.row]cell.drinkLabel.text = menureturn cell}
這樣子按下模擬器畫面就會顯示如下:
傳資料到下一頁
傳資料到下一頁有很多方法,這個範例將使用 IBSegueAction的方法。
首先,先切換到 Main StoryBoard,按+(Library)選取 View Controller,然後拉到 StoryBoard 空白處
結果如下
再來在新的 View Controller 上加一個 Label,讓 Menu TableView Controller 的飲料名稱可以讀取,然後顯示在 Label上。
一樣,按+選 Label 拉到 View Controller。
將 Label 的尺寸設為width 300,hight 50,字體 30, 置中
然後為新的 View Controller 建立新的 Class File,
一樣按 command + N,選 Cocoa Touch Class,將 Subclass of 選擇 UIViewController,Class 命名為DrinkLabelViewController
然後將新的 View Controller 的 class 指定為DrinkLabelViewController
將 Label 拉 IBOutlet ,命名為drinkNameLabel
在DrinkLabelViewController定義從上一頁要傳過來的資料為飲料名稱的字串, 並定義初始值
let drinkName: Stringinit?(coder: NSCoder, drinkName: String) {self.drinkName = drinkNamesuper.init(coder: coder)}required init?(coder: NSCoder) {fatalError("init(coder:) has not been implemented")}
ViewDidLoad Function 裡要指定 drinkNameLabel 顯示的字串就是 drinkName
override func viewDidLoad() {super.viewDidLoad()drinkNameLabel.text = drinkName}
這樣,DrinkLabelViewController 的程式碼就完成了
建立 @IBSegueAction 並輸入程式碼
回到MenuTableViewController
將+ uibutton 拉 Sequew 到 DrinkLabelViewController, 選 Show
就會出現 Seque 了
從 seque 按滑鼠右鍵建立@IBSegueAction
命名為showDrinkName, arguments 選 sender
在@IBSegueAction func showDrinkName(_ coder: NSCoder) -> DrinkLabelViewController? 輸入
let button = sender as? UIButtonif let point: CGPoint = button?.convert(.zero, to: tableView), let indexpath = tableView.indexPathForRow(at: point) {return DrinkLabelViewController(coder: coder, drinkName: menus[indexpath.row])} else {return nil}
程式碼說明如下:
let button = sender as? UIButton
因為我們是利用 UIbutton 建立 seque,按 button 之後就可以顯示及傳送資料到下一頁,所以我們將 sender 轉型為 UIButton。
if let point: CGPoint = button?.convert(.zero, to: tableView), let indexpath = tableView.indexPathForRow(at: point)
button?.convert
這個 funtion是讓app 知道按鈕的位置,從 tableView 的 坐標(0,0)到按鈕的位置,這樣就可以知道是第幾個 cell 的按鈕。
tableView.indexPathForRow(at: point)
回傳的是 cell 的indexpath 的值,包含了 cell 的 section 跟 row 值。
因為 point 跟 indexPath是 optional 的值,所以用 if let
,如果有值的話,就執行以下程式碼,如果沒有的話就回傳 nil。
return DrinkLabelViewController(coder: coder, drinkName: menus[indexpath.row])
當有 indexpath 值的時候,就可以利用 indexpath.row
值來知道是第幾行,那 menus[indexpath.row]
就是那一行的飲料名稱。
這樣就可以將 cell 裡的資料,使用 UIButton 將資料傳到下一頁了。
最終結果如下
參考資料
完整程式檔 at GitHub