[iOS] 간단한 알람 앱 만들기

David·2023년 4월 25일
0
post-thumbnail

간단한 알람 앱이지만, 만들면서 어려웠던 부분이 있어 추후에 다시 보고자 기록합니다!

기능 설명:

  1. 홈 화면에서 + 버튼 누르면 알람 추가 뷰로 이동.
  2. 알람 추가 뷰에서 원하는 시간을 고르고 저장을 누르면 홈 화면으로 데이터 전달.
  3. 전달받은 데이터를 홈 화면에서 테이블뷰로 표시.
  4. 현재 시각과 저장한 알람 시간이 동일하면 Alert 창 띄우기.

import UIKit

// ViewController(메인뷰) 코드

class ViewController: UIViewController {
    
    var timePickerData: [String] = [] // 전달받은 데이터 배열로 저장
    
    var isAlertOn = false // alert 창 
    
    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
        
        // 매 초 updateTime 함수 실행하여 현재시간과 전달 받은 데이터 비교, 시간 일치하면 alert창 띄우기.
        Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
    }
    
    @objc func updateTime() {
        let formatter = DateFormatter()
        formatter.dateFormat = "a hh:mm"
        formatter.locale = Locale(identifier: "ko_KR") // 오전, 오후로 포맷 변경
        let currentTime = formatter.string(from: Date()) // 현재 시간
        
        if isAlertOn {
            return
        }
        
        for data in timePickerData {
            if data == currentTime {
                self.isAlertOn = true
                
                Timer.scheduledTimer(timeInterval: 60.0, target: self, selector: #selector(self.timerOn), userInfo: nil, repeats: false) // 60초 후 alert 창 종료 
                
                let alert = UIAlertController(title: "알림", message: "설정된 시간입니다.", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "확인", style: UIAlertAction.Style.default) {UIAlertAction in})
                self.present(alert, animated:true, completion: nil)
            }
        }
        
    }
    
    @objc func timerOn() {
        isAlertOn = false
    }
    
    @IBAction func moveVCButton(_ sender: UIBarButtonItem) {
        guard let addAlarmVC = storyboard?.instantiateViewController(withIdentifier: "AlarmEditViewController") as? AlarmEditViewController else {return}
        addAlarmVC.delegate = self
        self.navigationController?.pushViewController(addAlarmVC, animated: true)
    }
}

extension ViewController: AlarmDelegate {
    func alarmDelegate(data: String) {
        timePickerData.append(data) // 전달받은 데이터, 배열로 값 추가
        self.tableView.reloadData() // 리로드하여 화면에 표시
    }
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return timePickerData.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmTableViewCell", for: indexPath) as! AlarmTableViewCell
        cell.AlarmTableViewCell.text = timePickerData[indexPath.row]
        return cell
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }
}
import UIKit
// AlarmEditViewController(알람 추가 뷰) 코드

protocol AlarmDelegate: AnyObject {
    func alarmDelegate(data: String)
}

class AlarmEditViewController: UIViewController {
    @IBOutlet weak var dismissLabel: UILabel!
    weak var delegate: AlarmDelegate? 
    var alarmData: String?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let dismissTapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissClicked))
        dismissLabel.addGestureRecognizer(dismissTapGesture)
        dismissLabel.isUserInteractionEnabled = true
    }
    
    @objc func dismissClicked(sender: UITapGestureRecognizer) {
        dismiss(animated: true)
    }
    
    @IBAction func saveButton(_ sender: UIButton) {
        guard let alarmData = alarmData else { return }
        self.delegate?.alarmDelegate(data: alarmData)
        print("저장버튼: \(alarmData)")
        print("delegate: \(delegate!)")
        self.navigationController?.popViewController(animated: true)
    }
    
    @IBAction func timePicker(_ sender: UIDatePicker) {
        let formatter = DateFormatter()
        formatter.dateFormat = "a hh:mm"
        formatter.locale = Locale(identifier: "ko_KR")
        alarmData = formatter.string(from: sender.date)
        print("alarmData: \(alarmData!)")
    }
}

어려웠던 부분: Delegate을 이용한 화면 간 데이터 전달, 그리고 tableView.reload..

저는 이 기능을 구현하면서 Delegate를 이용해봐야겠다 라고 생각했어요, 아직 Delegate가 익숙치 않기 때문에요.
하단에 서술한 대로 Delegate로 데이터 전달을 구현했지만, 메인뷰에는 데이터가 보이지 않아 많이 헤맸는데요.
알고 보니, 배열에 데이터를 추가한 후 테이블뷰 리로드를 해주지 않아 보이지 않았던 문제라 테이블뷰 리로드 메서드를 실행해주었더니 뷰가 잘 보였습니다.

Delegate 활용 방법:

본 앱 같은 경우는 알람 추가 뷰(AlarmEditViewController)에서 메인 뷰에 데이터를 전달하고 있어요.

  1. 알람 추가 뷰에 protocol 선언. 프로토콜 내 메서드를 선언만 하고 구현하지는 않습니다. 구현은 데이터를 전달 받은 뷰(메인뷰)에서 할거에요.
protocol AlarmDelegate: AnyObject {
    func alarmDelegate(data: String)
}
  1. 알람 추가 뷰 내에 delegate 변수 선언, 프로토콜 채택.
weak var delegate: AlarmDelegate?
  1. 데이터를 전달할 부분에서 delegate 변수를 활용하여 데이터 전달. 여기서는 저장 버튼을 클릭 시, 메인뷰에 전달했어요.
@IBAction func saveButton(_ sender: UIButton) {
        guard let alarmData = alarmData else { return }
        self.delegate?.alarmDelegate(data: alarmData) // 메인뷰에 데이터 전달.
        print("저장버튼: \(alarmData)")
        print("delegate: \(delegate!)")
        self.navigationController?.popViewController(animated: true)
    }
  1. 데이터를 메인 뷰에 전달해줬으니, 메인 뷰에서 데이터를 받아 원하는 기능 구현.
    메인뷰에서 프로토콜(AlarmDelegate)을 채택해줘야 해요. 그러면 메소드를 구현하라고 알림이 뜨니 원하는 기능 구현. 여기서는 전달 받은 데이터를 배열에 담아줬어요.
extension ViewController: AlarmDelegate {
    func alarmDelegate(data: String) {
        timePickerData.append(data)
        self.tableView.reloadData()
    }
}

전체 코드는 제 깃허브에서 보실 수 있어요.

1개의 댓글

comment-user-thumbnail
2023년 4월 25일

정말 훌륭합니다 !!

답글 달기