#33 Use UITableViewController with dynamic prototypes to imitate Apple Music

左圖為模擬器跑起來的畫面,右圖為目前 Apple Music 的截圖畫面

先前在製作表格都是使用 static cells,一格一格從 Storyboard 去設定各個 cell 的圖片及文字,今天要來記錄如何直接從程式去設定每一格 cell 的內容。

Target

  1. 使用 Dynamic prototypes 從程式設計表格
  2. 客製表格內的元件

Process

Step 1: Add component to Main storyboard

由於是從程式去設計表格,所以在 Main storyboard 的畫面看起來非常簡單,就是一個 Tab Bar Controller、一個 Navigation Controller 和一個 TableView Controller,然後 TableView Controller 裡的 cell 新增一個 ImageView、兩個 Label 、一個 Button,就醬。

Step 2: Add Cocoa Touch Class

如何增加這裡就不再贅述了,但要特別提醒的一點是,除了新增 UITableViewController class 以外,由於這次要客製化表格內容,所以必須還要再新增一個 UITableViewCell class。

新增完之後,記得要到 Identity Inspector 去改 Class,包含 TableViewCell 也要一併改掉。

Step 3: Add IBOutlet into the new UITableViewCell class

這裡要特別提一下一個很容易忘記的地方,以往我們拉 IBOutlet 都會拉到 ViewController class 裡面,但因為這次是 TableViewController,所以我們應該直接拉到 TableViewController class 裡面嗎?

Wrong!

我們的目標是要讓每一個 cell 裡面的圖都顯示各自所對應的專輯畫面,如果我們拉一個 IBOutlet 到整個 TableViewController class,這樣似乎有點奇怪,為什麼會奇怪?

我們程式裡面只有一個變數,但這個變數要同時顯示不一樣的圖片,這好像說不通。

所以應該要拉到 TableViewCell 的 class 裡面才對,之後用 indexPath.row 去讀取指定列的指定圖片即可。

可以點選 Content View 後,利用上圖右上角處去切換 TableViewCell class 和 TableViewController class。

Step 4: Coding

再來就是回到 TableViewController class 去完成剩下的 code 了。由於這是第一次在 TableViewController class 寫 code 。所以這邊還是記錄一下過程。

當你打開時,可以看到除了原本我們熟悉的 viewDidLoad() 以外,可以看到多了兩段程式碼如下:

override func numberOfSections(in tableView: UITableView) -> Int {// #warning Incomplete implementation, return the number of sectionsreturn 0}override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {// #warning Incomplete implementation, return the number of rowsreturn 0}

可以猜得到,第一段最後 return 的數字代表有幾個 section,第二段最後 return 的數字代表每一個 section 裡面有幾個 row。若依照 Apple Music 的介面來看,有一個 section ,至於幾個 row 就看你有幾筆資料要呈現,所以我會先建立一個 Array 儲存資料。

var cellInfos: [CellInfo] = [CellInfo(albumImage: "pic1", songName: "出走", artist: "蔡健雅"),CellInfo(albumImage: "pic2", songName: "我一直都在這裡 (電影《熊出沒·狂野大陸》主題曲)", artist: "徐佳瑩"),CellInfo(albumImage: "pic3", songName: "Permission to Dance", artist: "防彈少年團"),CellInfo(albumImage: "pic4", songName: "NDA", artist: "Billie Eilish"),CellInfo(albumImage: "pic5", songName: "Motley Crew", artist: "Post Malone"),CellInfo(albumImage: "pic6", songName: "could cry just thinkin about you (Full Version)", artist: "Troye Sivan"),CellInfo(albumImage: "pic7", songName: "NOW I'M ALONE (feat. Sofía Valdés)", artist: "HONNE"),CellInfo(albumImage: "pic8", songName: "Stay", artist: "The Kid LAROI & Justin Bieber"),CellInfo(albumImage: "pic9", songName: "Crowd Go Crazy", artist: "John Legend"),CellInfo(albumImage: "pic10", songName: "並不是夕陽美景 (Songs Of Stories 03.)", artist: "Control T"),CellInfo(albumImage: "pic11", songName: "Stranger Things (feat. Karra)", artist: "Lizzy Wang"),CellInfo(albumImage: "pic12", songName: "Wrecked", artist: "Imagine Dragons"),CellInfo(albumImage: "pic13", songName: "Better Off Without You (feat. 瘦子E.SO)", artist: "吳卓源"),CellInfo(albumImage: "pic14", songName: "In My Toes", artist: "陳芳語"),CellInfo(albumImage: "pic15", songName: "Jurassic Ride", artist: "恐龍的皮"),CellInfo(albumImage: "pic16", songName: "潮隙", artist: "AD & 四枝筆"),CellInfo(albumImage: "pic17", songName: "You for Me", artist: "Sigala & Rita Ora"),CellInfo(albumImage: "pic18", songName: "Yellow Cab", artist: "DPR LIVE"),CellInfo(albumImage: "pic19", songName: "We Know (feat. BG8LOCC & Allen Flex)", artist: "婁峻碩"),CellInfo(albumImage: "pic20", songName: "夢想的台北", artist: "魏妙如"),CellInfo(albumImage: "pic21", songName: "プリクエル", artist: "Omoinotake"),CellInfo(albumImage: "pic22", songName: "Bad Habits", artist: "Ed Sheeran")]

然後將剛剛的兩段程式改成如下:

override func numberOfSections(in tableView: UITableView) -> Int {// #warning Incomplete implementation, return the number of sectionsreturn 1}override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {// #warning Incomplete implementation, return the number of rowsreturn cellInfos.count}

改成 cellInfos.count 而不寫死數字的好處是,如果之後我新增或移除 cellInfos array 裡面的內容,就不用再回來改這裡了。

再來呢,除了這兩段程式碼,你可以看到一對 Apple 幫你寫的註解,看到這裡覺得 Apple 真的是 so sweet! 他把一些我們可能常用到的程式都寫好了,我們只要把其中一段前後的註解符號拿掉就可以直接用了!484 hen 棒!

今天用到的程式碼是下面這段,把這整段的前後註解先拿掉~

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}

再來記錄一下我裡面寫的 code 如下:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(AppleMusicTableViewCell.self)", for: indexPath) as? AppleMusicTableViewCell else {return UITableViewCell()}let cellInfo = cellInfos[indexPath.row]cell.albumImageView.image = UIImage(named: cellInfo.albumImage)cell.songNameLabel.text = cellInfo.songNamecell.artistLabel.text = cellInfo.artist// Configure the cell...return cell}

這裡有一個彼得大大提到的重點:

  1. guard let : 這個用法有點像 if let ,用來確保我的 cell 有順利產生,且成功轉型成我所指定的 AppleMusicTableViewCell ,否則就 return 一個空的 UITableViewCell()

還有一個也是很容易忘記的地方,就是要記得改 Identifier,上圖兩處紅色框框一定要對得起來,不然是生不出正確的 cell 來的。

到這邊應該就差不多囉~

Free talk

這次因為就設計單一頁面,沒有其他分頁,所以 Tab bar 也只有一頁可以選, Navigation bar 也沒有 back 按鍵囉~

--

--