# iOS | Swift/--- Project

[Alarm] 1. 기본 알람 목록 및 추가화면 구현, AlarmManager, 삭제 기능 추가

jiniz.ll 2022. 2. 25. 20:56
  • 개발 일자 : 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. 알람 섹션 분리 및 정렬