SwiftUI - 타이머 만들기

SonagiDev·2022년 10월 23일
0

SwiftUI

목록 보기
1/1
post-thumbnail

상점 뷰를 개발중에 타이머 기능이 필요하여 간단하게 글로 남겨봅니다.
전체 코드와 완성 화면은 글 하단에 있습니다.

구현 목표

HH:mm:ss의 형태로 1초씩 줄어드는 타이머

Reference

https://www.youtube.com/watch?v=_WJzpPgHkhg

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로 선언합니다.

2. 남은 초를 포맷에 맞게 변환

    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))
        }
    }
}

이렇게 되면

이런 형태가 나오게 됩니다.

Step 3. 남은 시간 계산하기

일단 남은 시간이 있으려면 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
    }
}

완성 화면

Reference
https://www.youtube.com/watch?v=_WJzpPgHkhg

profile
꿈이 매우 큽니다

0개의 댓글