今天開始,我們要進入下一個有趣的主題時鐘。
先來看看成果:
這裡我們透過 Delegate
來傳遞事件,每當資料有變化,就重新載入並刷新 TableView
。
// MARK: - Delegate
// 當新增鬧鐘後,重新載入資料
func didAddNewAlarm() {
loadAlarms()
tbvData.reloadData()
}
// 當編輯鬧鐘後,重新載入資料
func didUpdateAlarm() {
loadAlarms()
tbvData.reloadData()
}
// 當刪除鬧鐘後,重新載入資料
func didDeleteAlarm() {
loadAlarms()
tbvData.reloadData()
}
// 當點選鬧鐘音效頁面後,回傳選擇的音效名稱
func didTapPushToSound() {
let soundVC = SoundViewController()
soundVC.delegate = self
navigationController?.pushViewController(soundVC, animated: true)
}
Tips:每一次的操作,都要確保 UI 和資料保持同步,否則列表就會和實際的鬧鐘設定脫節
我們在 Storyboard
裡放入一個 UITableView
來顯示所有鬧鐘,並透過 IBOutlet
與程式碼連接。
接下來只要設定好 DataSource
與 Delegate
,就能讓資料跑進這個表格中。
// MARK: - IBOutlet
@IBOutlet weak var tbvData: UITableView!
在 viewDidLoad()
中,我們要做幾件重要的事:
設定 UI
與 Navigation Bar
。
請求通知權限。
監聽鬧鐘觸發的通知。
載入現有鬧鐘資料。
// MARK: - LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
setUI()
setupNavigationBar()
requestNotificationPermission()
// 載入鬧鐘資料
NotificationCenter.default.addObserver(self, selector: #selector(showAlarmAlert(_:)), name: NSNotification.Name("AlarmDidFire"), object: nil)
loadAlarms()
tbvData.reloadData()
}
我們把 Navigation Bar
的設定做好,上面有「新增」和「編輯」按鈕:
// MARK: - UI Setting
func setupNavigationBar() {
navigationController?.navigationBar.barTintColor = .systemGray6
// 利用UIImage設定右邊的按鈕為加號的圖案,讓後設定他的動作為`addTapped`的function
navigationItem.rightBarButtonItem =
UIBarButtonItem(image: UIImage(systemName: "plus"),
style: .plain,
target: self,
action: #selector(addTapped))
// 左邊按鈕則是用title設定他的文字為編輯,並設定他的動作為`editTapped`
navigationItem.leftBarButtonItem =
UIBarButtonItem(title: "編輯",
style: .plain,
target: self,
action: #selector(editTapped))
// 設定成我誠品畫面中的那樣,並且當中間tableView存放鬧鐘的地方往上滑動時,他會變為在左右 Button 的中間
navigationController?.navigationBar.prefersLargeTitles = true
// 然後再設定我們需要他顯示什麼字,比如我們設定的字為鬧鐘
navigationItem.title = "鬧鐘"
}
func setUI() {
tbvData.dataSource = self
tbvData.delegate = self
tbvData?.register(UINib(nibName: "MainTableViewCell", bundle: nil),forCellReuseIdentifier: "MainTableViewCell")
}
鬧鐘要能夠在背景提醒使用者,就必須向系統請求推播權限:
//請求通知權限
func requestNotificationPermission() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
// 請求成功印出成功獲取
if granted {
print("Notification permission granted")
// 請求失敗印出獲取失敗
} else {
print("Notification permission denied")
}
}
}
以下幾個方法處理了新增鬧鐘、編輯模式切換,以及鬧鐘響起時的提示:
// MARK: - IBAcion
// 當點選右上角的加號按鈕時,會跳出新增鬧鐘的畫面
@objc func addTapped() {
let addAlarmVC = AddAlarmViewController()
addAlarmVC.delegate = self
let navController = UINavigationController(rootViewController: addAlarmVC)
self.present(navController, animated: true)
}
// 首先我們要建立一個 isEditing 參數
var isEdting: Bool = false
// 當點選左上角的編輯按鈕時,會切換編輯模式
@objc func editTapped() {
isEdting.toggle()
tbvData.setEditing(isEdting, animated: true)
// 利用剛開始設定的布林參數做變動
navigationItem.leftBarButtonItem?.title = isEdting ? "完成" : "編輯"
// 遍歷所有當前可見的表格視圖單元格,並將其隱藏的開關控件設置為編輯狀態。
tbvData.visibleCells.forEach { cell in
if let switchControl = (cell as? MainTableViewCell)?.swTime {
switchControl.isHidden = isEdting
}
}
}
// 當鬧鐘響起時,顯示一個警告對話框
@objc func showAlarmAlert(_ notification: Notification) {
print("收到 AlarmDidFire 通知")
guard let noti = notification.object as? UNNotification else { return }
let content = noti.request.content
let alert = UIAlertController(title: content.title, message: content.body, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "確定", style: .default))
present(alert, animated: true)
}
// 當開關切換時,更新鬧鐘的狀態
@objc func alarmSwitchChange(_ sender: UISwitch) {
let row = sender.tag
let alarm = alarms[row]
print("開關切換:第 \(row) 筆,狀態為 \(sender.isOn)")
// 更新 Realm 中的開關狀態(如有需要)
let realm = try! Realm()
try! realm.write {
alarm.isEnabled = sender.isOn
}
}
今天我們先完成了時鐘 App的基本架構,包含導航列的設定、TableView 的資料顯示以及通知權限的請求等重要步驟。