DatePicker로 시간을 설정하는 카운트 다운 타이머 앱.
보는 강의가 UIKit으로 구현하여 SwiftUI로 바꿔보려고 했는데 SwiftUI에서는 카운트 다운 모드가 없었다.
이참에 SwiftUI에서 UIKit 사용하는 방법도 공부해야지 룰루~
Count Down Timer 모드로 설정하고 시간 간격은 1분으로 설정했다.
Storyboard에서 알아야 할 것은 이 정도.
타이머를 동작시키기 위해서 DispatchSourceTimer를 사용하였다.
var timer: DispatchSourceTimer?
var duration = 60 // DatePicker에서 설정한 시간
var currentSeconds = 0 // 카운트 다운 될 시간
func startTimer() {
current = duration
if timer == nil {
timer = DispatchSource.makeTimerSource(queue: .main)
// 이벤트 핸들러에서 작업하는 내용에 따라 스레드 지정.
// 1초마다 해야하는 작업이 UI 작업이므로 queue를 main thread로 설정.
timer?.schedule(deadline: .now(), repeating: 1)
// deadline은 타이머가 시작된지 몇 초 후에 작업을 시작할 건지
// repeating은 반복 주기로 단위는 sec
timer?.setEventHandler(handler: { [weak self] in
// 타이머가 동작할 때마다 실행되어야 할 작업을 여기에 작성한다.
// 이 앱에서는 남은 시간을 00:00:00 형태로 표시하고, -> String(format: "%02d", 숫자)
// 진행 상태를 상태바로 표시하며,
// 토마토 이미지를 360도씩 돌려준다.
guard let self = self else { return }
self.current -= 1
self.timerLabel.text = self.formattedTimeString()
self.progressView.progress = Float(self.currentSeconds) / Float(self.duration)
startAnimation()
if self.currentSeconds <= 0 { // 타이머 종료
self.stopTimer()
}
})
self.timer?.resume() // 타이머 시작
}
}
timer의 nil을 체크하는 이유는 타이머를 중단시킬 때 작업을 취소한 후 메모리 해제까지 해주는데
화면이 사라졌을 때도 타이머가 계속 동작하지 않도록 하기 위해서이다.
그리고 주의할 점은 타이머를 중단시킬 때.
enum TimerStatus {
case start
case pause
case end
}
func stopTimer() {
if timerStatus == .pause {
timer?.resume()
}
timer?.cancel()
timer = nil // 메모리 해제
}
우선 timer는 resume(), suspend(), cancel() 함수로 각각 실행, 중지, 취소시킬 수 있다.
만약 suspend() 함수를 호출하여 타이머를 일시중지 시켜놓고 cancel() 함수를 호출하면 Runtime Error 발생.
suspended object의 참조를 해제하려고 하면서 발생한 오류이다.
즉, timer가 동작중일 때 작업을 취소하고 메모리를 해제시키도록 한다.
Timer의 cancel 여부를 확인할 수 있는 API는 제공하지만, suspend 상태 확인 API는 제공되지 않아
enum으로 Timer의 상태값을 관리하도록 했다.
추가로 도움이 된 부분.
보통 view의 isHidden 속성으로 view의 visibility를 조절하는데
자연스러운 변화를 위해 UIView.animate으로 alpha 값을 변경시키면 fade in/out 효과를 줄 수 있다.
'iOS > App' 카테고리의 다른 글
Delegate: AnyObject (0) | 2023.12.23 |
---|