我們今天先從 MainView 來做教學,之前學過的功能我就簡單帶過讓各位可以做練習
我們透過前面設定好的架構我們已經有了 MainView 的 Controller 以及 tableViewCell 了
接下來我們一個接一個帶大家完成!
在接下來介紹時我不會完整的貼整個程式碼,而是專注於介紹新的程式碼用途!
透過前面的設定應該大家不會有什麼問題,資料庫套件記得也要 import 喔!
import UIKit
import RealmSwift
import UserNotifications
按照前面設定的要求我們設定一個 tableView 來顯示!
別忘記要從 xib 拉元件過來 swift 設定喔!
@IBOutlet weak var tableView: UITableView!
接著同樣我們需要設定一個變數來儲存資料
var alarms: [AlarmData] = []
別忘了要記得設定 tableView 的代理跟 tableView 的註冊
func setUI() {
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: "MainTableViewCell",
bundle: nil),
forCellReuseIdentifier: "MainTableViewCell")
}
我們要設定一個 @objc
用來處理每個鬧鐘開關狀態改變的事件
@objc func alarmSwitchChange(_ sender: UISwitch) {
let index = sender.tag
let realm = try! Realm()
try! realm.write {
alarms[index].isEnabled = sender.isOn
}
}
// 載入鬧鐘資料
func loadAlarms() {
let realm = try! Realm()
// 取得所有鬧鐘資料並按時間排序
alarms = Array(realm.objects(AlarmData.self).sorted(byKeyPath: "alarmTime",
ascending: true))
print("目前鬧鐘數量:", alarms.count)
tableView.reloadData()
}
// 刪除鬧鐘
func deleteAlarm(_ alarm: AlarmData,
at indexPath: IndexPath) {
let realm = try! Realm()
// 刪除通知請求
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [alarm.creatTime])
for index in 0..<7 {
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["\(alarm.creatTime)_\(index)"])
}
try! realm.write {
realm.delete(alarm)
}
alarms.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
// enumerated 會返回一個包含索引和值的元組序列
// compactMap 會過濾掉 nil 值並返回一個新的陣列
func repeatDaysText(_ repeatDays: List<Bool>) -> String {
let days = ["週日", "週一", "週二", "週三", "週四", "週五", "週六"]
let arr = Array(repeatDays)
let selected = arr.enumerated().compactMap { $0.element ? days[$0.offset] : nil }
if arr == [false, true, true, true, true, true, false] {
return "平日"
} else if arr == [true, false, false, false, false, false, true] {
return "週末"
} else if arr.allSatisfy({ $0 }) {
return "每天"
} else {
return selected.isEmpty ? "永不" : selected.joined(separator: "、")
}
}
比較複雜的 func 到這裡我在這邊帶各位做完啦~
剩下轉換時間制跟取得上下午的 func 讓大家自主練習看看!
我們需要特別寫一個 viewWillAppear 來做每當畫面刷新就重新讀一次資料
讓我們的畫面可以保持顯示最新資料
// 每次畫面出現時重新載入鬧鐘資料
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
loadAlarms()
}
首先設定的部分跟之前一樣就展示一下,不多做介紹
extension MainViewController: UITableViewDelegate, UITableViewDataSource {
...
}
接著我們來看程式碼內容
我們這次會用到
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainTableViewCell", for: indexPath) as! MainTableViewCell
let alarm = alarms[indexPath.row]
cell.lbTime.text = get12HourTime(alarm.alarmTime)
cell.lbNoon.text = getNoon(alarm.alarmTime)
let name = alarm.name.isEmpty ? "鬧鐘" : alarm.name
let repeatText = repeatDaysText(alarm.repeatDays)
cell.lbSubtitle.text = "\(name),\(repeatText)"
cell.swAlarm.isOn = alarm.isEnabled
cell.swAlarm.tag = indexPath.row
cell.swAlarm.removeTarget(nil, action: nil, for: .allEvents)
cell.swAlarm.addTarget(self, action: #selector(alarmSwitchChange(_:)), for: .valueChanged)
cell.swAlarm.isHidden = isEditingMode
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let alarm = alarms[indexPath.row]
let editAlarmVC = AddAlarmViewController(nibName: "AddAlarmViewController", bundle: nil)
editAlarmVC.alarmToEdit = alarm
editAlarmVC.selectedSound = alarm.sound
editAlarmVC.delegate = self
let navController = UINavigationController(rootViewController: editAlarmVC)
self.present(navController, animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let alarm = alarms[indexPath.row]
deleteAlarm(alarm, at: indexPath)
}
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive, title: "刪除") { [weak self] (_, _, completionHandler) in
guard let self = self else { return }
let alarm = self.alarms[indexPath.row]
self.deleteAlarm(alarm, at: indexPath)
completionHandler(true)
}
deleteAction.backgroundColor = .red
return UISwipeActionsConfiguration(actions: [deleteAction])
}
protocl 的部分我們就先給各位看程式碼
明天介紹 AddAlarmView 時再跟各位做講解 func
先知道我們要的功能有
在做了這些動作後需要重新刷新畫面上的鬧鐘顯示資料
這樣就很清楚了吧!
extension MainViewController: AlarmUpdateDelegate {
func didAddNewAlarm() {
print("didAddNewAlarm called")
loadAlarms()
}
func didEditAlarm() {
print("didEditAlarm called")
loadAlarms()
}
func didDeleteAlarm() {
print("didDeleteAlarm called")
loadAlarms()
}
}