iT邦幫忙

2025 iThome 鐵人賽

DAY 23
0
Mobile Development

我將點燃Swiftの大海系列 第 23

Day23. Swift一定要會のios鬧鐘復刻實作篇 (4)

  • 分享至 

  • xImage
  •  

我們今天先從 MainView 來做教學,之前學過的功能我就簡單帶過讓各位可以做練習

MainView

我們透過前面設定好的架構我們已經有了 MainView 的 Controller 以及 tableViewCell 了
接下來我們一個接一個帶大家完成!
在接下來介紹時我不會完整的貼整個程式碼,而是專注於介紹新的程式碼用途!

Controller

import

透過前面的設定應該大家不會有什麼問題,資料庫套件記得也要 import 喔!

import UIKit
import RealmSwift
import UserNotifications

IBoutlet

按照前面設定的要求我們設定一個 tableView 來顯示!

別忘記要從 xib 拉元件過來 swift 設定喔!

    @IBOutlet weak var tableView: UITableView!

變數

接著同樣我們需要設定一個變數來儲存資料

    var alarms: [AlarmData] = []

setUi

別忘了要記得設定 tableView 的代理跟 tableView 的註冊

    func setUI() {
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(UINib(nibName: "MainTableViewCell", 
                                 bundle: nil), 
                           forCellReuseIdentifier: "MainTableViewCell")
    }

IBAction

我們要設定一個 @objc 用來處理每個鬧鐘開關狀態改變的事件

    @objc func alarmSwitchChange(_ sender: UISwitch) {
        let index = sender.tag
        let realm = try! Realm()
        try! realm.write {
            alarms[index].isEnabled = sender.isOn
        }
    }

function

  1. 首先我們要先來寫用來讀取鬧中資料的 func ,記得寫完要包進去 viewDidLoad 喔!
    // 載入鬧鐘資料
    func loadAlarms() {
        let realm = try! Realm()
        // 取得所有鬧鐘資料並按時間排序
        alarms = Array(realm.objects(AlarmData.self).sorted(byKeyPath: "alarmTime", 
                                                            ascending: true))
        print("目前鬧鐘數量:", alarms.count)
        tableView.reloadData()
    }
  1. 接著我們要寫一個用來刪除鬧鐘用的 func ,按照下面的設定一步一步做喔!
  • 先刪掉通知排程刪掉資料庫內容
  • 刪掉資料庫內容
  • 刪掉陣列資料
  • 從 tableView 刪掉 Row
    // 刪除鬧鐘
    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)
    }
  1. 最後我們要寫一個來讀取重複天數的 func ,並且可以判斷專案要求的特殊情況
    利用 enumerated 和 compactMap 來做陣列的過濾
        // 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 讓大家自主練習看看!

  1. func get12HourTime
  2. func getNoon

LifeCycle

我們需要特別寫一個 viewWillAppear 來做每當畫面刷新就重新讀一次資料
讓我們的畫面可以保持顯示最新資料

    // 每次畫面出現時重新載入鬧鐘資料
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        loadAlarms()
    }

extension

tableView 設定

首先設定的部分跟之前一樣就展示一下,不多做介紹

extension MainViewController: UITableViewDelegate, UITableViewDataSource {
    ...
}

接著我們來看程式碼內容
我們這次會用到

  • cellForRowAt
    ( return cell內容 )
  • didSelectRowAt
    ( 處理點擊行後要進入編輯頁面 )
  • commit editingStyle: UITableViewCell.EditingStyle()
    ( 用來設定之前 NavigationBar 的編輯 )
  • trailingSwipeActionsConfigurationForRowAt()
    ( 用來做之前學過的滑動刪除功能 )
    
    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])
    }

portocol: addalarm

protocl 的部分我們就先給各位看程式碼
明天介紹 AddAlarmView 時再跟各位做講解 func
先知道我們要的功能有

  • 新增鬧鐘
  • 編輯鬧鐘
  • 刪除鬧鐘

在做了這些動作後需要重新刷新畫面上的鬧鐘顯示資料
這樣就很清楚了吧!

extension MainViewController: AlarmUpdateDelegate {
    func didAddNewAlarm() {
        print("didAddNewAlarm called")
        loadAlarms()
    }
    func didEditAlarm() {
        print("didEditAlarm called")
        loadAlarms()
    }
    func didDeleteAlarm() {
        print("didDeleteAlarm called")
        loadAlarms()
    }
}


上一篇
Day22. Swift一定要會のios鬧鐘復刻實作篇 (3)
下一篇
Day24. Swift一定要會のios鬧鐘復刻實作篇 (5)
系列文
我將點燃Swiftの大海25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言