# iOS | Swift/--- Project

[Alarm] #-1. 사운드 옵션 화면 추가 및 선택한 알람 사운드 전달하기

jiniz.ll 2022. 1. 26. 23:32
  • 개발 일자 : 2022. 1. 19

 

이건 좀 써둬야겠다고 생각이 드는 것이 한 번씩 있는데 요 알람 앱을 만들면서 좀 더 그런 생각이 많이 들었다

 

간단한 것들 중에서도 처음 해보는 방식이거나 생각해봐야 할 것들이 끊임없이 있더라

 

(느닷없이 왼쪽 엄지 손가락 관절이 아픈데,, 왜지)

 

암튼 생각 정리가 필요해서 오늘부터 적기 시작..!

이전 날짜도 중요한 건 적어둬야지

 

알람 사운드 옵션 화면 추가

  • 스토리보드에 사운드 옵션 선택을 위한 화면(AlarmSoundVC) 추가
  • 번외로 셀 선택했을 때 deselect 하는 코드를 tableView(didDeselectRowAt:) 메서드에 넣고 있었다는 걸 깨닫고 tableView(didSelectRowAt:) 메서드로 변경해줌

 

  • AlarmSoundVC 에 사운드 목록을 표시할 테이블 뷰를 추가해주었음
import UIKit

class AlarmSoundViewController: UIViewController {

    @IBOutlet weak var alarmSoundTableView: UITableView!
    
    private var soundList = [
        "Alarm Clock",
        "Maple Leaf Rag"
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()

        alarmSoundTableView.delegate = self
        alarmSoundTableView.dataSource = self
    }
}

extension AlarmSoundViewController: UITableViewDelegate, UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return soundList.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmSoundTableViewCell", for: indexPath)
        
        var content = cell.defaultContentConfiguration()
        content.text = soundList[indexPath.row]
        
        cell.contentConfiguration = content
        
        return cell
    }
}

 

선택한 알람 사운드 전달하기

AlarmDetailVC

  • 알람 추가 / 편집 화면에서 이전에는 시간만 전달을 했었는데 옵션 기능을 추가함에 따라 알람 객체를 넘기는 것으로 수정함
  • AlarmListVC AlarmDetailVC 로 알람 정보를 전달할 때도 마찬가지
  • 따라서 AlarmManager 에 데이터를 전달할 때도 시간 문자열 값이 아닌 Alarm 객체를 넘기도록 전체적으로 수정함

 

  • 알람 목록에서 상세 화면으로 넘어올 때 시간 값을 전달해줬었는데 주고 받는 데이터를 알람 객체로 바꾸면서 여러 부분의 코드에서 시간 값에 대해 Date 타입과 String 타입의 변환이 자주 일어나게 되어 번거로워 짐

Alarm 구조체에 저장되는 시간 타입을 Date 로 변경 후 연산 프로퍼티를 사용하여 displayTime 을 문자열 타입으로 정의함

 

  • 스토리보드 상에서 AlarmDetailVC 에서 AlarmSoundVC 로 연결되는 segue 를 추가했고
  • 해당 segue 를 실행하게 되면 현재 지정되어 있는 사운드 인덱스 값을 전달함
  • AlarmOptionSelecting 타입의 delegate 에 자신을 지정함

AlarmOptionSelecting 을 채택하게 됨

AlarmSoundVC 에서 선택된 사운드 정보를 받아오기 위함

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "AlarmSoundSegue" {
        let alarmSoundVC = segue.destination as? AlarmSoundViewController
        alarmSoundVC?.delegate = self
        alarmSoundVC?.selectedSoundIndexPath = alarm.soundIndexPath
    }
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)
    
    switch indexPath.row {
    case 2:
        self.performSegue(withIdentifier: "AlarmSoundSegue", sender: nil)
    default:
        break
    }
}

 

  • 또한 AlarmSoundVC 에서 특정 사운드를 선택하고 상세 화면으로 돌아오게 되면 채택한 AlarmOptionSelecting 의 alarmSoundSelected() 함수가 실행되는데 
  • 이 때 선택된 사운드의 인덱스 값을 받아오게 되고 선택한 값에 따라서 상세 화면의 사운드 옵션 셀의 디테일 값을 설정하게 됨
extension AlarmDetailViewController: AlarmOptionSelecting {
    
    func alarmSoundSelected(indexPath: IndexPath) {
        alarm.soundIndexPath = indexPath
        alarmOptions[0][2].content = alarmManager.getSoundTitle(at: indexPath)
        let indexPath = IndexPath(row: 2, section: 0)
        alarmOptionTableView.reloadRows(at: [indexPath], with: .automatic)
    }
}

 

AlarmManager

  • 해당 알람의 사운드 정보를 저장하기 위한 IndexPath 프로퍼티를 추가함

 

  • 처음에 사운드 목록에 대한 정보를 AlarmSoundVC 에서 관리한다고 생각하고 사운드 제목이랑 선택 여부?? 같은걸 저장하는 구조체를 갖고 그에 대한 사운드 목록을 만들었었는데 뭔가 좀 이상하다는 생각이 들어서 (타이틀은 고정 상수 값이고 선택 여부는 알람 마다 다른데??)
  • 생각해보니 사운드 타이틀 목록은 다른 곳에서도 쓰이기 때문에 AlarmManager 로 옮겼고 (이와 관련해서 필요한 기능이 많아지면 SoundManager 같은걸 만들어야 하나 고민) 각 알람에서 어떤 사운드가 선택되었는지는 알람이 관리하는 게 맞기 때문에 위에 언급한 것과 같이 알람 구조체에 사운드 정보를 가져올 수 있는 soundIndexPath 프로퍼티를 추가함
  • 현재 Sound 구조체도 있고 soundList 가 Sound 목록을 갖게 되는데 title 정보 외에 필요한 정보가 없다면 문자열 타입으로 변경할 수도 있음

 

  • indexPath 를 사용해서 사운드 제목을 가져오는 메소드 및 사운드 리스트 항목 개수를 반환하는 함수 및 프로퍼티를 추가함

 

AlarmSoundVC

  • 화면에서 특정 사운드를 선택하고 화면을 종료했을 때 선택된 사운드 정보를 전달하기 위한 AlarmOptionSeleting 프로토콜을 추가
  • 선택된 사운드 정보를 갖기 위한 selectedSoundIndexPath 프로퍼티가 추가됨

 

  • 해당 화면에는 저장 버튼이 아닌 뒤로가기 버튼만 있기 때문에 화면이 사라지기 전에(viewWillDisappear 메소드에서) 위임자(delegate)의 alarmSoundSelected 함수를 실행함
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    delegate?.alarmSoundSelected(indexPath: selectedSoundIndexPath)
}

 

  • 이건 번외로 사운드 옵션을 선택한 체크 마크를 하나만 유지하기 위해 어떻게 해야할지 찾아보다가 처음 사용했던 방식이 다음과 같고
참고. ios - UITableView Checkmark ONLY ONE Row at a Time - Stack Overflow
이 때, 스토리보드 상에서 AlarmSoundCell 의 Selection 속성이 None 으로 되어 있는 것이 좋음
그렇지 않으면 선택된 셀이 하이라이트 되기 때문에 일반적으로 tableView(didSelectRowAt:) 메소드가 실행되었을 때 deselectRow() 메소드를 실행해주는데 여기서 이 메소드를 실행하면 tableView(didDeselectRowAt:) 메소드가 실행이 안되었음 (체크 해제가 안됨)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
    tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}

func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {

    tableView.cellForRow(at: indexPath)?.accessoryType = .none
}
  • 이후에 선택된 사운드 인덱스 정보를 갖고 있으면 된다는 것을 깨닫고 다음과 같이 수정하였음
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    ...
    if selectedSoundIndexPath == indexPath {
        cell.accessoryType = .checkmark
    } else {
        cell.accessoryType = .none
    }
    ...
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    
    selectedSoundIndexPath = indexPath
    tableView.reloadData()
}