상점 뷰를 개발중에 타이머 기능이 필요하여 간단하게 글로 남겨봅니다.
전체 코드와 완성 화면은 글 하단에 있습니다.
HH:mm:ss의 형태로 1초씩 줄어드는 타이머
struct ContentView: View {
let date = Date()
@State var timeRemaining : Int = 100
var body: some View {
ZStack {
Text(String(timeRemaining))
.font(.system(size: 50))
}
}
}
뷰 생성시 시간을 고정해두는 date,
1초마다 줄어드는 남은 시간을 의미하는 timeRemaining을 State Property로 선언합니다.
func convertSecondsToTime(timeInSeconds: Int) -> String {
let hours = timeInSeconds / 3600
let minutes = (timeInSeconds - hours*3600) / 60
let seconds = timeInSeconds % 60
return String(format: "%02i:%02i:%02i", hours,minutes,seconds)
}
convertSecondsToTime 함수는 초(second)를 Int로 받아 HH:mm:ss 형태의 String으로 return합니다.
시간, 분, 초에 해당하는 계산을 수행하고 원하는 포맷으로 String을 만드는데요,
String(format: "%02i:%02i:%02i", hours,minutes,seconds)
이 포맷은 '빈 자리가 0으로 채워지는 2자리의 정수' 사이에 콜론이 들어가는 형태를 의미합니다.
struct ContentView: View {
let date = Date()
@State var timeRemaining : Int = 100
var body: some View {
ZStack {
Text(convertSecondsToTime(timeInSeconds:timeRemaining)
.font(.system(size: 50))
}
}
}
이렇게 되면
이런 형태가 나오게 됩니다.
일단 남은 시간이 있으려면 target time이 있어야합니다
target time을 정하는 것은 많은 방법이 있는데요, 저는 아까 선언해둔 date에 값을 더하는 방법을 사용해보겠습니다.
let calendar = Calendar.current
let targetTime : Date = calendar.date(byAdding: .second, value: 3800, to: date, wrappingComponents: false) ?? Date()
남은 시간을 구하는 방법으로는 Date의 내장함수 timeIntervalSince를 이용하겠습니다.
let remainSeconds = Int(targetTime.timeIntervalSince(date))
self.timeRemaining = remainSeconds
계산된 remainSeconds를 timeRemaining에 할당하겠습니다.
func calcRemain() {
let calendar = Calendar.current
let targetTime : Date = calendar.date(byAdding: .second, value: 3800, to: date, wrappingComponents: false) ?? Date()
let remainSeconds = Int(targetTime.timeIntervalSince(date))
self.timeRemaining = remainSeconds
}
이 과정을 calcRemain 함수로 정의합니다. 이 함수는 처음 한 번만 실행해주고 (onAppear에 달아놓는 방법을 선택하겠습니다)timeRemaining을 1초에 1씩 깎는 로직을 더해주면 타이머가 완성되겠네요!
Step 4. 남은 시간 줄이기
우선 Timer를 이용하기 위해 Combine을 import합니다.
import Combine
그리고 Text에 onReceive를 달아주어 이벤트를 받아보도록 하겠습니다
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
ZStack {
Text(convertSecondsToTime(timeInSeconds:timeRemaining))
.font(.system(size: 50))
.onReceive(timer) { _ in
timeRemaining -= 1
}
}
struct ContentView: View {
let date = Date()
@State var timeRemaining : Int = 100
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
ZStack {
Text(convertSecondsToTime(timeInSeconds:timeRemaining))
.font(.system(size: 50))
.onReceive(timer) { _ in
timeRemaining -= 1
}
}
.onAppear {
calcRemain()
}
}
func convertSecondsToTime(timeInSeconds: Int) -> String {
let hours = timeInSeconds / 3600
let minutes = (timeInSeconds - hours*3600) / 60
let seconds = timeInSeconds % 60
return String(format: "%02i:%02i:%02i", hours,minutes,seconds)
}
func calcRemain() {
let calendar = Calendar.current
let targetTime : Date = calendar.date(byAdding: .second, value: 3810, to: date, wrappingComponents: false) ?? Date()
let remainSeconds = Int(targetTime.timeIntervalSince(date))
self.timeRemaining = remainSeconds
}
}