iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 20
0
Software Development

利用Swift 4開發iOS App,Daily Work List系列 第 20

Day 20. Develop Week Page UITableView With Sections

今天我們來完成以日期分群組的TableView,完成後會長這樣
https://ithelp.ithome.com.tw/upload/images/20181020/20111916BJWmGUUHKs.png

首先,我們先來準備要用的資料內容。建立一個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 ""
    }
}

上一篇
Day 19. Develop Week Page Storyboard & View Controller
下一篇
Day 21. Develop Month Page Storyboard & CollectionView Setting
系列文
利用Swift 4開發iOS App,Daily Work List31

尚未有邦友留言

立即登入留言