iOS/App

Timer

소토리텔러 2023. 12. 21. 01:13

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 효과를 줄 수 있다.