今天我們來完成以日期分群組的TableView,完成後會長這樣
首先,我們先來準備要用的資料內容。建立一個struct結構,用來記錄TableView所需的資料
struct EventGroup {
// 是否展開
var open: Bool = true
// 標題
var title: String = ""
// event array
var eventArray: [Event] = [Event]()
}
接著,把昨天程式裡的Array換成EventGroup的Array型態
// var eventList: [Event] = [Event]()
var eventGroupArray: [EventGroup] = [EventGroup]()
然後,撈資料changeDate()時要把資料裝進去這個eventGroupArray裏頭
// 本週的第一天,我們期望是星期一
var firstDate: Date!
// 撈取資料庫資料
let weekArray = sqlManager.queryWeekByDate(date: dateFormatter.string(from: datePicker.date))
if weekArray.count < 1 {
// 資料庫沒資料,要新增該週資料
// 查詢今天星期幾,星期日為1、星期一為2...以此類推
let weekDay = calendar.component(.weekday, from: datePicker.date)
// 查詢今天為這個月的第幾個禮拜
var weekNum = calendar.component(.weekOfMonth, from: datePicker.date)
var startDate: String!
var endDate: String!
if weekDay == 2 {
// 若為星期一,則我們顯示的開始日即為該日,結束日為六日後的星期日
startDate = dateFormatter.string(from: datePicker.date)
endDate = dateFormatter.string(from: calendar.date(byAdding: .day, value: 6, to: datePicker.date)!)
} else if weekDay > 2 {
// 若為星期二以後,則我們顯示的開始日要為該星期的星期一,結束日為星期日
startDate = dateFormatter.string(from: calendar.date(byAdding: .day, value: 2 - weekDay, to: datePicker.date)!)
endDate = dateFormatter.string(from: calendar.date(byAdding: .day, value: 8 - weekDay, to: datePicker.date)!)
} else if weekDay < 2 {
// 若為星期日,則我們顯示的開始日要為六天前的星期一,結束日為該日,且記得weekNum要減一,因為calendar以每個星期日為切點
startDate = dateFormatter.string(from: calendar.date(byAdding: .day, value: -6, to: datePicker.date)!)
endDate = dateFormatter.string(from: datePicker.date)
weekNum = weekNum - 1
}
// 新增該星期資料
sqlManager.insertWeek(work_id: "\(weekFormatter.string(from: datePicker.date))_\(weekNum)", start_date: startDate, end_date: endDate)
// 指定本週第一天的時間
firstDate = dateFormatter.date(from: startDate)
// 清空Note欄位
noteText.text = ""
} else {
// 若有資料,則將該週起始日與Note帶入
firstDate = dateFormatter.date(from: weekArray[0].start_date)!
noteText.text = weekArray[0].note
}
// 清空eventGroupArray的資料,方便後面重新設定
eventGroupArray.removeAll()
// 從起始日開始加入一個星期的資料
for i in 0...6 {
let date = calendar.date(byAdding: .day, value: i, to: firstDate)!
let title = "\(showDateFormatter.string(from: date)) (\(getString(week: calendar.component(.weekday, from: date))))"
let eventArray = sqlManager.queryEventByDate(date: dateFormatter.string(from: date))
let eventGroup = EventGroup(open: true, title: title, eventArray: eventArray)
eventGroupArray.append(eventGroup)
}
撈完資料後,要來設定TableView的內容,將資料呈現出來
// get the count of sections you are going to display in your tableView
func numberOfSections(in tableView: UITableView) -> Int {
// 設定分群的個數,理論上就是7,一個禮拜的天數,但我們還是期望動態顯示依照eventGroup的數量呈現
return eventGroupArray.count
}
// get the count of elements of section you are going to display in your tableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// 顯示群組內資料的筆數
if eventGroupArray[section].open {
// 當該群組是有被展開的情況下,要計算該天的event數
// 這邊注意一下,因為我把section title也一起當作一行cell,因此要加一噢
return eventGroupArray[section].eventArray.count + 1
} else {
// 同理,當未展開的時候要保留1讓section title顯示
return 1
}
}
// assign the values in your array variable to a cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// 這裏要設定顯示的cell內容
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! EventTableViewCell
if indexPath.row == 0 {
// 如果是第一筆,就視為section title
cell.titleLabel.text = eventGroupArray[indexPath.section].title
cell.backgroundColor = UIColor.lightGray
cell.accessoryType = .none
} else {
// 其他筆,就當作一般event cell顯示,所以記得撈array資料時要把索引減一噢
let dataIndex = indexPath.row - 1
cell.backgroundColor = UIColor.white
cell.titleLabel.text = eventGroupArray[indexPath.section].eventArray[dataIndex].name
cell.id = eventGroupArray[indexPath.section].eventArray[dataIndex].id
if eventGroupArray[indexPath.section].eventArray[dataIndex].finish {
// 如果該event已完成,要顯示勾勾
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
}
return cell
}
// register when user taps a cell
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 此method為當使用這點擊該cell時,要做的對應動作
let cell = tableView.cellForRow(at: indexPath) as! EventTableViewCell
if indexPath.row == 0 {
// 如果是第一筆,就視為section title
// 當section title被點擊,會toggle下方的eventArray
if eventGroupArray[indexPath.section].open {
eventGroupArray[indexPath.section].open = false
} else {
eventGroupArray[indexPath.section].open = true
}
cell.accessoryType = .none
let section = IndexSet.init(integer: indexPath.section)
eventTableView.reloadSections(section, with: .none)
} else {
// 當event cell被點擊時,會toggle顯示完成與否,並修改資料庫資料
if eventGroupArray[indexPath.section].eventArray[indexPath.row - 1].finish {
eventGroupArray[indexPath.section].eventArray[indexPath.row - 1].finish = false
cell.accessoryType = .none
sqlManager.updateEventFinishById(id: cell.id, finish: false)
} else {
eventGroupArray[indexPath.section].eventArray[indexPath.row - 1].finish = true
cell.accessoryType = .checkmark
sqlManager.updateEventFinishById(id: cell.id, finish: true)
}
}
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// 開啟該cell row可以被滑動顯示按鈕
return true
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
if indexPath.row > 0 {
// 若為event cell,要加入刪除與編輯的按鈕
let delete = UITableViewRowAction(style: .normal, title: "Delete") { action, indexPath in
self.sqlManager.deleteEventById(id: self.eventGroupArray[indexPath.section].eventArray[indexPath.row - 1].id)
self.eventGroupArray[indexPath.section].eventArray.remove(at: indexPath.row - 1)
// 將畫面上的cell移除
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.fade)
}
delete.backgroundColor = UIColor.red
let edit = UITableViewRowAction(style: .normal, title: "Edit") { action, indexPath in
if let controller = self.storyboard?.instantiateViewController(withIdentifier: "EditEventViewController") as? EditEventViewController {
controller.event_id = self.eventGroupArray[indexPath.section].eventArray[indexPath.row - 1].id
self.present(controller, animated: true, completion: nil)
}
}
edit.backgroundColor = UIColor.lightGray
return [edit, delete]
} else {
// 反之,section title則不做動作
return nil
}
}
這樣就大致完成了喔!
再加上一個顯示星期的function 就好,看起來會比較舒服些
func getString(week: Int) -> String {
switch week {
case 2:
return "星期一"
case 3:
return "星期二"
case 4:
return "星期三"
case 5:
return "星期四"
case 6:
return "星期五"
case 7:
return "星期六"
case 1:
return "星期日"
default:
return ""
}
}