#34 cell 的重覆利用 — to-do list 的完成打勾
不負責旅遊清單練習
作業來源:
涵蓋的部分:
基本版
1. 產生 array 資料(待辦事項,想讀的書,想去玩的國家等),顯示在表格裡。
2. 點選 cell 顯示 / 取消勾選。觀察表格上下捲動後,原本打勾的 cell 是否變沒打勾,原本沒打勾的 cell 是否變打勾。
3. 其他:點選灰色空心的愛心,會變成紅色實心的愛心,再次點選會變回灰色空心愛心。
練習目標:
Apple 程式會利用重複使用 cell,來增加運作時的效能,但當在表格上做註記(如打勾,愛心等)時,練習不因表格上下捲動而把註記位置給打亂
做法摘要:
產生資料:
為了減少產生資料花費的時間,使用 ChatGPT 產生資料,當產生的資料很怪或不是很滿意時,可以按下方中間 “Regenerate response” 重新產生答案,或是把問題打得更明確一些。
🌟 ChatGPT 相關教學
然後就是把資料複製到 Xcode 裡,加上兩個變數其型別為Bool去產生表格,
在 true 時顯示打勾或顯示愛心,false 時取消勾選或刪除愛心。
一樣先產生兩個 swift file 及 struct
最後產生的資料長這個樣子,兩個 Bool 設定為 false
var Countries: [Country] = [
Country(name: "JAPAN", attrction: [
Attraction(name: "東京塔", location: "東京都港區", transportation: "地鐵", visit: false, like: false),
Attraction(name: "上野動物園", location: "東京都台東區", transportation: "地鐵", visit: false, like: false),
Attraction(name: "富士山", location: "静岡縣", transportation: "巴士", visit: false, like: false),
Attraction(name: "淺草寺", location: "東京都台東區", transportation: "地鐵", visit: false, like: false),
Attraction(name: "新宿", location: "東京都新宿區", transportation: "地鐵", visit: false, like: false),
Attraction(name: "原宿", location: "東京都渋谷區", transportation: "地鐵", visit: false, like: false),
Attraction(name: "日本橋", location: "東京都中央區", transportation: "地鐵", visit: false, like: false),
Attraction(name: "東京迪士尼樂園", location: "千葉縣", transportation: "巴士", visit: false, like: false),
Attraction(name: "江戶村", location: "東京都墨田區", transportation: "地鐵", visit: false, like: false),
Attraction(name: "秋葉原", location: "東京都千代田區", transportation: "地鐵", visit: false, like: false),
Attraction(name: "銀座", location: "東京都中央區", transportation: "地鐵", visit: false, like: false),
Attraction(name: "晴空塔", location: "東京都港區", transportation: "地鐵", visit: false, like: false),
Attraction(name: "神田明神", location: "東京都千代田區", transportation: "地鐵", visit: false, like: false),
Attraction(name: "涉谷", location: "東京都渋谷區", transportation: "地鐵", visit: false, like: false),
Attraction(name: "明治神宮", location: "東京都渋谷區", transportation: "地鐵", visit: false, like: false)]),
Country(name: "KOREA", attrction: [
Attraction(name: "首爾塔", location: "首爾市中心", transportation: "地鐵", visit: false, like: false),
Attraction(name: "景福宮", location: "景福門", transportation: "地鐵", visit: false, like: false),
Attraction(name: "光州塔", location: "光州市中心", transportation: "公車", visit: false, like: false),
Attraction(name: "首爾城堡", location: "首爾市中心", transportation: "地鐵", visit: false, like: false),
Attraction(name: "海邊藝術村", location: "釜山市西部海岸", transportation: "公車", visit: false, like: false),
Attraction(name: "秀川村", location: "忠清北道", transportation: "公車", visit: false, like: false),
Attraction(name: "漢江花園城", location: "首爾市東南部", transportation: "地鐵", visit: false, like: false),
Attraction(name: "東大門市場", location: "首爾市東部", transportation: "地鐵", visit: false, like: false),
Attraction(name: "釜山海洋世界", location: "釜山市西部海岸", transportation: "公車", visit: false, like: false),
Attraction(name: "慶州海灘", location: "慶州市", transportation: "公車", visit: false, like: false),
Attraction(name: "首爾科學博物館", location: "首爾市中心", transportation: "地鐵", visit: false, like: false),
Attraction(name: "松山文化村", location: "首爾市中心", transportation: "地鐵", visit: false, like: false),
Attraction(name: "南怡島", location: "釜山市西南部", transportation: "公車", visit: false, like: false),
Attraction(name: "韓國牛肉餐廳", location: "首爾市中心", transportation: "地鐵", visit: false, like: false),
Attraction(name: "濟州島", location: "忠清北道", transportation: "飛機", visit: false, like: false)
])
]
製作藍色勾勾:
藍色勾勾是利用 Table View Cell 裡的 accessory 內建的樣式,共有五種,這次只用到 none 跟 checkmark
當點選到特定 cell 時,希望可以觸發勾選或是取消打勾,但其實程式裡的變化是:
順序 1. 點選時先去改變 Bool 值 → 順序 2. Bool 值改變時再去改變顯示介面
所以在 cellForRow 的 function 裡(順序 2):
當變數 visit 的 Bool 值變成 true 則 accessoryType = .checkmark,變 false 則 accessoryType = .none
if Countries[indexPath.section].attrction[indexPath.row].visit == false {
cell.accessoryType = .none
} else {
cell.accessoryType = .checkmark
}
再來,設定點到 cell 時要讓 Bool 值改變(順序 1),
所以在 didSelectRow 的 function 裡,運用運算子! 對 Bool 值取相反值,
假設:A 是 Bool 型別,則觸發 A = !A (讀作:把相反 A 的值存入 A)時,
A 原本是 true 會變成 false,false 會變成 true。
在這裡程式會變這樣:
Countries[indexPath.section].attraction[indexPath.row].visit = !Countries[indexPath.section].attraction[indexPath.row].visit
🌟 因為 value type 的關係,得把它們(Countries & attraction)直接寫在一起。參考:
點選了 cell 改變了 Bool 值,最後,要再加上一行:
tableView.reloadData( ),去更新表格
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// !:Bool之間轉換,不是true就是false
Countries[indexPath.section].attrction[indexPath.row].visit = !Countries[indexPath.section].attrction[indexPath.row].visit
//Reloads the rows and sections of the table view.
tableView.reloadData()
}
製作點取變成紅色愛心:
愛心的部分則是使用 Button 搭配 SF Symbols,搭配顏色的變化
跟藍色勾勾觀念類似,在 cellForRow 的 function 裡:
當變數 like 的 Bool 值變成 true 則 heartButton.setImage 呈現紅色愛心,變 false 則呈現灰色空心愛心
這裡做了一個 enum 的 rawValue 字串對應 SF Symbols 的字串
enum Heart: String {
case like = "heart.fill"
case unlike = "heart"
}
搭配顏色變化
if Countries[indexPath.section].attrction[indexPath.row].like == false {
cell.heartButton.setImage(UIImage(systemName: "\(Heart.unlike.rawValue)"), for: .normal)
cell.heartButton.tintColor = .systemGray3
} else {
cell.heartButton.setImage(UIImage(systemName: "\(Heart.like.rawValue)"), for: .normal)
cell.heartButton.tintColor = UIColor.systemRed
}
一樣在最後,在按下愛心 Button 時,去改變 Bool 值,所以拉 IBAction func 到 Table View Controller,搭配 convert function 用座標判斷觸發的 cell 。
參考這篇
Button 的 IBAction func 程式,一樣也要輸入 tableView.reloadData( ) 去更新表格
@IBAction func heartButtonChosen(_ sender: UIButton) {
let point = sender.convert(CGPoint.zero, to: tableView)
if let indexPath = tableView.indexPathForRow(at: point) {
Countries[indexPath.section].attraction[indexPath.row].like = !Countries[indexPath.section].attraction[indexPath.row].like
}
tableView.reloadData()
}
在 cell 的 reuseIdentifier 使用 Extension 跟 computed property 去取代輸入ID字串
import Foundation
import UIKit
extension UITableViewCell {
static var reuseIdentifier: String {"\(Self.self)"}
}
cellForRow 程式碼
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: AttractionTableViewCell.reuseIdentifier, for: indexPath) as? AttractionTableViewCell else {
fatalError("dequeueReusableCell AttractionTableViewCell failed")
}
if Countries[indexPath.section].attraction[indexPath.row].visit == false {
cell.accessoryType = .none
} else {
cell.accessoryType = .checkmark
}
if Countries[indexPath.section].attraction[indexPath.row].like == false {
cell.heartButton.setImage(UIImage(systemName: "\(Heart.unlike.rawValue)"), for: .normal)
cell.heartButton.tintColor = .systemGray3
} else {
cell.heartButton.setImage(UIImage(systemName: "\(Heart.like.rawValue)"), for: .normal)
cell.heartButton.tintColor = UIColor.systemRed
}
configureCell(indexPath, cell)
return cell
}
細節調整:
在 Table View 的 Attributes Inspector 可以調整 Style,共有
🌟 Plain
🌟 Grouped
🌟 Inset Grouped
將在以下說明。
Plain
如文章上方的GIF所示,滑動時螢幕上方一直有一根 bar 在那裡。
Grouped
滑動時螢幕上方不會有 bar
Inset Grouped
把表格變成圓角的格式,記得表格跟 Table View 背景顏色要不一樣才看得出來。