#149 讓 table view 多選,而且每個 section 只能選一個 cell
最近有同學問到一個有趣的問題,如何讓 table view 可以多選,而且每個 section 只能選一個 cell。
比方我們想做一個高級西式餐廳點餐 App,點餐畫面如下。客人點餐時必須在各個分類裡各選一道菜。為了他的健康和荷包著想,怕他吃太多肉或花太多錢,我們限制他不能在一個分類裡選多道菜。
解答
接下來我們將從最基本的 table view 開始,一步步說明如何實現這樣的功能。
先實作單選的表格
MealCourse
定義餐點的資料型別,利用變數 data 儲存餐點的 array。
import Foundationenum MealCategory: String {
case soup
case mainCourse = "main course"
case dessert
}struct MealCourse {
let category: MealCategory
let names: [String]
}extension MealCourse {
static var data: [Self] {
[
MealCourse(category: .soup, names: [
"巴黎日安洋蔥湯",
"生蠔鮮蝦濃湯"
]),
MealCourse(category: .mainCourse, names: [
"爐烤美國菲力牛排",
"爐烤法式香料小羔羊",
"爐烤起司波士頓龍蝦"
]),
MealCourse(category: .dessert, names: [
"蘋果塔",
"熔岩巧克力",
"起士千層派"
]),
]
}
}
MealTableViewController
定義負責顯示餐點表格的 MealTableViewController。
import UIKitclass MealTableViewController: UITableViewController { let mealCourses = MealCourse.data
override func viewDidLoad() {
super.viewDidLoad()
} override func numberOfSections(in tableView: UITableView) -> Int {
return mealCourses.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return mealCourses[section].names.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return mealCourses[section].category.rawValue
} override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MealCourseCell", for: indexPath)
let mealCourse = mealCourses[indexPath.section]
cell.textLabel?.text = mealCourse.names[indexPath.row]
return cell
}
}
storyboard 的畫面設定
執行 App
table view 完美地呈現餐點清單,不過 table view 預設是單選,所以客人很可憐,不能同時喝湯,吃肉,吃蛋糕。
將表格改成可以多選
將 table view 的 Selection 設為 Multiple Selection
- 方法1: 從 Interface Builder 設定
- 方法2: 從程式設定
tableView.allowsMultipleSelection = true
執行 App
Cool,現在我們可以多選了,可以同時喝湯,吃肉,吃蛋糕。
but 現在有個很大的問題,現在客人也能在一個 section 裡選多個 cell,所以他可以貪心地點三種不同的點心。為了客人的健康,我們一定要解決這個問題,讓一個 section 只能選一個 cell。
限制一個 section 只能選一個 cell
定義 table view delegate 的 function tableView(_:willSelectRowAt:),控制 cell 是否被選取。
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if let selectIndexPathInSection = tableView.indexPathsForSelectedRows?.first(where: {
$0.section == indexPath.section
}) {
tableView.deselectRow(at: selectIndexPathInSection, animated: false) }
return indexPath
}
當使用者點選 cell 時將觸發 tableView(_:willSelectRowAt:),回傳的 indexPath 會決定被選取的 cell。我們希望被點選的 cell 被選取,所以我們回傳 indexPath,因為參數 indexPath 代表目前被點選的 cell 位置。(ps: 若是回傳 nil,使用者點選的 cell 將不會被選取。)
不過除了選取 cell,我們還有一件重要的事要做。我們必須檢查使用者是否已選取 section 裡的其它 cell,然後呼叫 table view 的 deselectRow 取消選取,防止一個 section 出現多個被選取的 cell。
執行 App
Cool,現在 table view 可以多選,而且每個 section 只能選一個 cell,客人終於可以安心健康地享受大餐了。
One more thing,檢查每個 section 是否都有選取的 cell
為了實現真正的點餐功能,我們還要檢查每個 section 是否都有選取的 cell。就算你不喜歡喝湯,還是要選擇某一種湯,這樣廚師才不會難過。有興趣的朋友可進一步實現此功能。