- 개발 일자 : 2022. 1. 10
알람 앱을 만들기 시작한 이유는 다른 앱을 만드는 중이었는데,, 그 앱에 알람 기능을 넣는 방식을 고민하는 것이 넘 복잡하기도 하고 좀 더 한 기능에만 몰입해서 개발을 하고 싶어서 종종 그런 것들이 생길 때마다 MySamples 프로젝트에 추가하기 위해 만들기 시작했다. 알람이라는 기능이 단순히 말 그대로 ‘알람’ 앱에만 사용되지는 않는다. 생각보다 많은 앱들에서 ‘알림’을 주어야 하는 상황이 존재하고 그 방식이 비슷하겠다라는 생각이 들었다. 이 알람 앱은 가장 기본적인 ‘알람’ 앱을 구현해보고자 시작한 프로젝트이다.
이미 비슷한 화면들을 구현해왔기 때문에 첫날 구현한 화면들과 기능은 빠르게 추가된 것들이다
알람 목록 및 추가화면 구현
- AlarmListVC
- 우선 간단하게 알람 목록에 대한 화면을 만들었음
- Alarm 에 대한 구조체도 만들고, 알람 목록을 표시할 테이블 뷰도 추가했음
- 각 알람 셀을 위한 AlarmTableViewCell 도 만들었고
- 각각의 알람 셀을 선택했을 때 추가/편집화면으로 이동할 수 있도록 segue 설정을 해두었음
- 처음에는 alarm 데이터에 대해 AlarmListVC 에서 Alarm 배열 변수로 갖고 있었음
- 처음 알람을 생성하게 되면 기본적으로 알람이 ON 상태가 됨
- 각 셀을 제거할 수 있도록 하기 위해 tailingSwipeAction 에 Delete 액션도 추가해두었음
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let navigation = segue.destination as? UINavigationController {
let alarmDetailVC = navigation.topViewController as? AlarmDetailViewController
alarmDetailVC?.delegate = self
if segue.identifier == "AlarmEditSegue" {
alarmDetailVC?.title = "알람 편집"
alarmDetailVC?.editMode = true
if let selectedRow = alarmTableView.indexPathForSelectedRow?.row {
alarmDetailVC?.selectedRow = selectedRow
alarmDetailVC?.time = alarms[selectedRow].time.convertToDate()
}
}
}
}
extension AlarmListViewController: AlarmSaving {
func saveAlarm(time: String) {
alarms.append(Alarm(time: time))
alarmTableView.reloadData()
}
func editAlarm(at row: Int, time: String) {
alarms[row] = Alarm(time: time)
alarmTableView.reloadData()
}
}
extension AlarmListViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return alarms.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: AlarmTableViewCell.identifier, for: indexPath) as? AlarmTableViewCell else { return UITableViewCell() }
cell.configure(alarm: alarms[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let delete = UIContextualAction(style: .normal, title: "Delete") { _,_,success in
print("Delete")
success(true)
}
delete.backgroundColor = .red
return UISwipeActionsConfiguration(actions: [delete])
}
}
- AlarmDetailVC
- 알람을 추가하거나 편집하는 화면임
- 하지만 AlarmDetail 이라고 이름 지은 이유는 VC 이름에 추가와 편집이라는 말을 전부 넣기보다 어쨌든 알람에 대한 상세 설정을 하는 화면이기 때문에 좀더 포괄하는 이름으로 설정하였음
- 처음 Alarm 구조체에는 시간에 대한 정보를 문자열로 컨버팅하고 다시 상세화면에 보여질 때, Date 타입으로 변환하여 사용했음
- 새로 저장을 할 때와 다르게 편집을 할 때는 해당 셀의 위치 정보가 필요하기 때문에 두 메서드를 구분하였음
protocol AlarmSaving: AnyObject {
func saveAlarm(time: String)
func editAlarm(at row: Int, time: String)
}
@IBAction func didTapSaveButton(_ sender: UIBarButtonItem) {
time = alarmDatePicker.date
if editMode, let row = selectedRow {
delegate?.editAlarm(at: row, time: time.convertToString(format: .displayTime))
} else {
delegate?.saveAlarm(time: time.convertToString(format: .displayTime))
}
dismiss(animated: true, completion: nil)
}
AlarmManager 추가
- 알람을 관리하기 위해 AlarmManager 를 추가함!
- AlarmListVC 에 있던 알람 구조체 및 알람 목록을 가져왔고 알람 리스트에서 알람이 추가되거나 수정되었을 때, 최종적인 처리는 알람 매니저에서 관리 하도록 작성
- 이 때, 알람 매니저는 하나만 관리하기 위해 싱글톤 형식으로 shared 변수를 만들었음
- 알람 정보를 받아오거나 추가하거나, 스위치가 켜지거나 꺼졌을 때, 알람 시간이 변경되었을 때 수정하는 코드를 추가하였음
class AlarmManager {
static let shared = AlarmManager()
private var alarms: [Alarm] = [
Alarm(time: "오전 10:00"),
Alarm(time: "오후 3:20")
]
var count: Int {
get {
return alarms.count
}
}
func getAlarm(at index: Int) -> Alarm {
return alarms[index]
}
func add(alarm: Alarm) {
alarms.append(alarm)
}
func alarmSwitched(to isOn: Bool, alarmRowAt index: Int) {
alarms[index].isOn = isOn
}
func setAlarmTime(to time: String, at index: Int) {
alarms[index].time = time
}
}
- 이에 맞춰서 AlarmListVC 에서는 alarm 변수가 아닌 알람 매니저 인스턴스를 가지고 작업하도록 수정되었음
extension AlarmListViewController: AlarmSaving {
func saveAlarm(time: String) {
alarmManager.add(alarm: Alarm(time: time))
alarmTableView.reloadData()
}
func editAlarm(at row: Int, time: String) {
alarmManager.setAlarmTime(to: time, at: row)
alarmTableView.reloadData()
}
}
- 또한 AlarmTableViewCell 에서 온/오프 스위치가 동작했을 때, AlarmManager 에서 꺼지고 켜지는 것을 변경할 수 있도록 delegate를 추가함
// AlarmTableViewCell
protocol AlarmCellDelegate: AnyObject {
func alarmSwitched(to isOn: Bool, _ indexPath: IndexPath)
}
@IBAction func didTapOnOffSwitch(_ sender: UISwitch) {
guard let indexPath = indexPath else { return }
delegate?.alarmSwitched(to: sender.isOn, indexPath)
}
// AlarmListVC
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.delegate = self
cell.configure(alarm: alarmManager.getAlarm(at: indexPath.row))
return cell
}
extension AlarmListViewController: AlarmCellDelegate {
func alarmSwitched(to isOn: Bool, _ indexPath: IndexPath) {
alarmManager.alarmSwitched(to: isOn, alarmRowAt: indexPath.row)
}
}
알람 삭제 기능 추가
- AlarmListVC 에서 스와이프를 통해 Delete 버튼을 눌렀을 때, alarmManger 를 통해 데이터를 삭제하고 테이블 뷰를 리로드하여 화면에 보여주도록 수정
- AlarmManger 에서는 인덱스 정보를 통해 alarms 프로퍼티에서 해당 데이터를 삭제
// AlarmListVC
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let delete = UIContextualAction(style: .normal, title: "Delete") { [weak self] _, _, completion in
self?.alarmManager.delete(alarmRowAt: indexPath.row)
DispatchQueue.main.async {
self?.alarmTableView.reloadData()
}
completion(true)
}
delete.backgroundColor = .red
}
// AlarmManager
func delete(alarmRowAt index: Int) {
alarms.remove(at: index)
}
► 다음글 : Alarm 2. 알람 섹션 분리 및 정렬
'# iOS | Swift > --- Project' 카테고리의 다른 글
[Alarm] #+4. 알람 끄기 화면 구현 (0) | 2022.02.25 |
---|---|
[Alarm] #+3. 알람 레이블 화면 추가 (0) | 2022.02.25 |
[Alarm] #+2. 상세 화면에서 알람 삭제 기능 및 알람 시간 텍스트 표시 수정 (0) | 2022.02.03 |
[Alarm] #+1. 알람 편집 모드 구현 (0) | 2022.02.03 |
[Alarm] #-1. 사운드 옵션 화면 추가 및 선택한 알람 사운드 전달하기 (0) | 2022.01.26 |