(iOS)뽀모도로 타이머 앱 만들기

25412 단어 swiftiOSiOS

기능 상세

  • DatePicker 를 통해 타이머 시간을 설정한다.
  • 시작 버튼을 누르면 타이머가 시작되고 일시 정지를 누르면 타이머가 일시 정지된다.
  • 취소 버튼을 누르면 타이머가 종료된다.
  • 카운트 다운이 완료되면 알람이 울린다.

활용 기술

  • DispatchSourceTimer
  • UIViewAnimation

구현

  • 타이머를 구현할 때 DispatchSourceTimer 를 사용
    - 타이머를 만드는 함수의 인자값 중 queue 부분에 .main 을 선언하여 타이머가 메인 쓰레드에서 돌도록 한다.(UI와 관련된 부분은 모두 메인 쓰레드에서 담당한다고 한다.)
    - 타이머가 suspend 인 상태에서 nil 을 입력받게 되면 즉, 일시정지된 상태에서 취소를 누르게 되면 Runtime Error 가 발생하게 된다. 따라서 취소하기 전에 타이머의 상태를 resume 으로 만들어줘야 한다.
  • UIViewAnimation 을 사용하여 타이머를 시작하거나 취소할 때 부드럽게 화면 전환이 되도록 하였다.
    - 추가로, 타이머가 돌아가는 동안 뽀모도로 이미지가 회전하도록 구현하였다.
import UIKit
import AudioToolbox

enum TimerStatus {
    case start
    case pause
    case end
}

class ViewController: UIViewController {
    @IBOutlet weak var timerLabel: UILabel!
    @IBOutlet weak var progressView: UIProgressView!
    @IBOutlet weak var datePicker: UIDatePicker!
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var cancelButton: UIButton!
    @IBOutlet weak var toggleButton: UIButton!
    
    var duration = 60
    var timerStatus: TimerStatus = .end
    var timer: DispatchSourceTimer?
    var currentSeconds = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.configureToggleButton()
    }
    
    func setTimerInfoViewVisible(isHidden: Bool) {
        self.timerLabel.isHidden = isHidden
        self.progressView.isHidden = isHidden
    }
    
    func configureToggleButton() {
        self.toggleButton.setTitle("시작", for: .normal)
        self.toggleButton.setTitle("일시정지", for: .selected)
    }
    
    func startTimer() {
        if self.timer == nil {
            self.timer = DispatchSource.makeTimerSource(flags: [], queue: .main)
            self.timer?.schedule(deadline: .now(), repeating: 1)
            self.timer?.setEventHandler(handler: { [weak self] in
                guard let self = self else { return }
                self.currentSeconds -= 1
                let hour = self.currentSeconds / 3600
                let minutes = (self.currentSeconds % 3600) / 60
                let seconds = (self.currentSeconds % 3600) % 60
                self.timerLabel.text = String(format: "%02d:%02d:%02d", hour, minutes, seconds)
                self.progressView.progress = Float(self.currentSeconds) / Float(self.duration)
                UIView.animate(withDuration: 0.5, delay: 0, animations: {
                    self.imageView.transform = CGAffineTransform(rotationAngle: .pi)
                })
                UIView.animate(withDuration: 0.5, delay: 0.5, animations: {
                    self.imageView.transform = CGAffineTransform(rotationAngle: .pi * 2)
                })
                
                if self.currentSeconds <= 0 {
                    self.stopTimer()
                    AudioServicesPlaySystemSound(1005)
                }
            })
            self.timer?.resume()
        }
    }
    
    func stopTimer() {
        if self.timerStatus == .pause {
            self.timer?.resume()
        }
        
        self.timerStatus = .end
        self.cancelButton.isEnabled = false
        UIView.animate(withDuration: 0.5, animations: {
            self.timerLabel.alpha = 0
            self.progressView.alpha = 0
            self.datePicker.alpha = 1
            self.imageView.transform = .identity
        })
        self.toggleButton.isSelected = false
        self.timer?.cancel()
        self.timer = nil
    }
    
    @IBAction func tapCancelButton(_ sender: UIButton) {
        switch self.timerStatus {
        case .start, .pause:
            self.stopTimer()
            
        default:
            break
        }
    }
    
    @IBAction func tapToggleButton(_ sender: UIButton) {
        self.duration = Int(self.datePicker.countDownDuration)
        
        switch self.timerStatus {
        case .end:
            self.currentSeconds = self.duration
            self.timerStatus = .start
            UIView.animate(withDuration: 0.5, animations: {
                self.timerLabel.alpha = 1
                self.progressView.alpha = 1
                self.datePicker.alpha = 0
            })
            self.toggleButton.isSelected = true
            self.cancelButton.isEnabled = true
            self.startTimer()
            
        case .start:
            self.timerStatus = .pause
            self.toggleButton.isSelected = false
            self.timer?.suspend()
            
        case .pause:
            self.timerStatus = .start
            self.toggleButton.isSelected = true
            self.timer?.resume()
        }
    }
}

최종 화면

GitHub

https://github.com/pjs0418/PomodoroTimer

출처

패스트캠퍼스, 초격차 패키지 : 30개 프로젝트로 배우는 iOS 앱 개발 with Swift

좋은 웹페이지 즐겨찾기