我們今天先把昨天沒做的資料庫給補上:
import Foundation
import RealmSwift
class AlarmData: Object {
@Persisted var alarmTime: String = "" // 鬧鐘要響的時間
@Persisted var creatTime: String = "" // 創建鬧鐘的時間,方便進行排序
@Persisted var name: String = "" // 給自己設定的鬧鐘設定名稱,比如說:睡覺、工作
@Persisted var repeatDays: List<Bool> = List<Bool>() // 後面設定重複天數的true or false
@Persisted var sound: String = "" // 選擇鬧鐘響鈴聲的
@Persisted var isEnabled: Bool = true // 控制鬧鐘的開關
@Persisted var snoozeEnabled: Bool = true // 稍後提醒
@objc dynamic var isOn: Bool = true
// 讓我們可以在其他.swift檔也能使用參數
convenience init(alarmTime: String, creatTime: String, name: String, repeatDays: [Bool] = Array(repeating: false, count: 7), sound: String, snoozeEnabled: Bool = true) {
self.init()
self.alarmTime = alarmTime
self.creatTime = creatTime
self.name = name
self.sound = sound
self.repeatDays.append(objectsIn: repeatDays)
self.snoozeEnabled = snoozeEnabled
}
}
Tips:@Persisted:Realm
的屬性標註,表示這個欄位會被存入資料庫。repeatDays
:使用 List<Bool>
來儲存 7 天的重複狀態,例如 [true, false, ...]
表示每週哪些天會重複響鈴。creatTime
:用來當作唯一識別,刪除通知時也會用到。
除了新增與顯示資料外,刪除鬧鐘時我們必須:
Realm
中的資料TableView
以下是刪除邏輯:
// 刪除鬧鐘的功能
// alarms是用來存放所有鬧鐘的陣列
var alarms: [AlarmData] = []
// 刪除指定的鬧鐘
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)"]
)
}
// 刪除Realm資料庫中的鬧鐘
try! realm.write {
realm.delete(alarm)
}
// 移除alarms中的鬧鐘並更新tableView
alarms.remove(at: indexPath.row)
tbvData.deleteRows(at: [indexPath], with: .fade)
}
每次 App 啟動或資料變動時,都要從 Realm 把鬧鐘重新抓回來:
// 載入Realm中的鬧鐘資料
func loadAlarms() {
let realm = try! Realm()
let results = realm.objects(AlarmData.self).sorted(byKeyPath: "alarmTime", ascending: true)
alarms = Array(results)
}
為了呈現每筆鬧鐘資料,我們在 TableView
的 Cell 中顯示:
以下是程式碼:
// 設定tableView有幾個section
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
// 設定每個section裡有多少row
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
// 根據新增的鬧鐘數量決定有多少row
return alarms.count
}
/// 設定tableView每個cell要顯示的內容
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainTableViewCell", for: indexPath) as! MainTableViewCell
let alarm = alarms[indexPath.row]
// 12小時制時間顯示
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "zh_TW")
formatter.dateFormat = "HH:mm"
let date = formatter.date(from: alarm.alarmTime) ?? Date()
formatter.dateFormat = "a" // 上午/下午
let noonString = formatter.string(from: date)
formatter.dateFormat = "hh:mm" // 時間
let timeString = formatter.string(from: date)
cell.lbNoon.text = noonString
cell.lbTime.text = timeString
// 標籤與重複內容
let name = alarm.name.isEmpty ? "鬧鐘" : alarm.name
let daysOfWeek = ["週日", "週一", "週二", "週三", "週四", "週五", "週六"]
let selectedDays = alarm.repeatDays.enumerated().filter { $0.1 }.map { $0.0 }
let repeatTitle: String
if selectedDays.count == 2 && selectedDays.contains(0) && selectedDays.contains(6) {
repeatTitle = "假日"
} else if selectedDays.count == 5 && selectedDays == [1,2,3,4,5] {
repeatTitle = "平日"
} else if selectedDays.count == 0 {
repeatTitle = "永不"
} else if selectedDays.count == 7 {
repeatTitle = "每天"
} else {
let selectedNames = selectedDays.map { daysOfWeek[$0] }
repeatTitle = selectedNames.joined(separator: "、")
}
cell.lbClock.text = "\(name)・\(repeatTitle)"
// 開關
cell.swTime.isOn = alarm.isEnabled
cell.swTime.tag = indexPath.row
cell.swTime.addTarget(self, action: #selector(alarmSwitchChange(_:)), for: .valueChanged)
cell.swTime.isHidden = isEditing
// 移除箭頭
cell.accessoryType = .none
return cell
}
// 處理點選某一個row的動作
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 允許該section的row被選擇
tableView.allowsSelection = true
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)
}
// 處理進入編輯模式後刪除row的動作
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])
}
}
今天補上資料庫並完成基本資料操作,讓鬧鐘可以真正被儲存、載入與刪除。