Starbucks Caramel Frappuccino
본문 바로가기
  • 그래 그렇게 조금씩
SwiftUI/SwiftUI(Intermediate)

19. Timer 타이머

by Toughie 2023. 6. 13.

⭐️Timer 타이머⭐️

지난 시간 컴바인 개념을 떠올리며.. 타이머도 Publisher이다.

일정한 시간 간격마다 이벤트를 생성하기 때문이다.

이를 활용해서 카운트 다운, 시계, 디데이, 애니메이션, 자동 스와이프 등을 구현할 수 있다.

Timer

let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

every - 타이머 이벤트가 발생하는 간격 (여기서는 1초 마다)

on - 타이머 이벤트가 실행되는 큐를 지정. (여기서는 UI 업데이트 관련 작업을 하기에 메인 스레드로 지정)

in - 타이머 이벤트를 처리할 스케줄러(scheduler)를 지정. .common은 일반적인 실행 우선순위를 가짐.

(타이머 이벤트가 기본 실행 우선순위로 처리되어 일반적인 작업과 함께 실행될 수 있음)

 

.autoconnect() - 타이머 자동 연결. (타이머가 생성되면 자동으로 실행됨)


카운트 다운

10부터 1초에 1씩 줄어들며 시간이 다 되면 'Time's Up' 텍스트가 보임.

//  Created by Toughie on 2023/06/13.
//

import SwiftUI

struct TimerPrac: View {
    
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

    @State var count: Int = 10
    @State var finishedText: String?
    
        var body: some View {
        ZStack {
            
            Text(finishedText ?? "\(count)")
            
        }
        .onReceive(timer) { _ in
            if count <= 1 {
                finishedText = "Time's Up!"
            } else {
                count -= 1
            }
        }
    }
}

.onReceive(_ publisher:perform:)

Publisher의 값을 수신해서 해당 값에 대한 동작을 수행하는데 사용되는 메서드.

timer Publisher를 구독하고, 각 타이머 이벤트를 받았을 때 실행될 클로저를 정의한 것이다.

여기서는 따로 수신받은 값을 활용하지 않기에 와일드카드 처리함.


시계(DateFormatter)

1초씩 시간 확인 가능한 시계

//  Created by Toughie on 2023/06/13.
//

import SwiftUI

struct Clock: View {
    
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    @State var currentData: Date = Date()
    
    var dateFormatter: DateFormatter {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .medium
        return formatter
    }
    
    
    var body: some View {
        ZStack {
            
            Text(dateFormatter.string(from: currentData))
            
        }
        .onReceive(timer) { value in
            currentData = value
            
        }
    }
}

Computed Property 활용해서 DateFormatter 초기화

날짜, 시간 표기 방식을 다양하게 설정할 수 있다. (short, medium, long, full)


디데이(타이머 2)

카운트다운 연장선으로 몇시간 뒤, 며칠 뒤 까지 얼마나 남았는지 체크 가능

//  Created by Toughie on 2023/06/13.
//

import SwiftUI

struct Timer2: View {
    
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    @State var timeRemaining: String = ""
    
    let futureDate: Date = Calendar.current.date(byAdding: .hour, value: 1, to: Date()) ?? Date()
    
    func updateTimeRemaining() {
        let remaining = Calendar.current.dateComponents([.hour, .minute, .second], from: Date(), to: futureDate)
        let hour = remaining.hour ?? 0
        let minute = remaining.minute ?? 0
        let second = remaining.second ?? 0
        timeRemaining = "\(hour): \(minute): \(second)"
    }
    
    var body: some View {
        ZStack {
            
            Text(timeRemaining)
                .font(.system(size: 100, weight: .semibold, design: .rounded))
                .foregroundColor(.gray)
                .lineLimit(1)
                .minimumScaleFactor(0.1)
            
        }
        .onReceive(timer) { value in
            
            updateTimeRemaining()
            
        }
    }
}

let futureDate

-> 현재 날짜에서 1시간 만큼 더한 날짜를 나타냄 (by adding: .day)면 일 단위로 더하기 가능

 

func updateTimeRemaining()

현재 시간 Date()와 futureDate 사이의 시간 간격을 계산하고 시간, 분, 초 컴포넌트로 분해해서
문자열 보간법을 활용해 뷰에 표기하는 방식


로딩 애니메이션

로띠에서 흔히 보이는 공이 통통 거리는 로딩 애니메이션

//  Created by Toughie on 2023/06/13.
//

import SwiftUI

struct TimerAnimation: View {
    
    let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
    
    @State var count: Int = 0
    
    var body: some View {
        ZStack {
            
            HStack(spacing: 15) {
                Circle()
                    .fill(Color.red.opacity(0.5))
                    .offset(y: count == 1 ? -20 : 0)
                
                Circle()
                    .fill(Color.green.opacity(0.5))
                    .offset(y: count == 2 ? -20 : 0)
                Circle()
                    .fill(Color.blue.opacity(0.5))
                    .offset(y: count == 3 ? -20 : 0)
            }
            .frame(width: 150)
            .foregroundColor(.blue)
            
            .onReceive(timer) { value in
                
                withAnimation(.easeIn(duration: 0.5)) {
                    count = count == 3 ? 0 : count + 1
                }
                
            }
        }
    }
}

총 3초동안 진행되는 애니메이션, 매 초마다 각 원의 오프셋을 조정해주는 방식(withAnimation)

 

count = count == 3 ? 0 : count + 1

삼항 연산자 활용 할당방식 참고.

3초가 됐을때 count를 다시 0으로 바꿔주고 1씩 계속 증가 시킴


자동으로 탭뷰 넘기기

배너? 같은 느낌을 줄 때 활용할 수 있을듯

//  Created by Toughie on 2023/06/13.
//
import SwiftUI

struct AutoTabView: View {
    
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    @State var count: Int = 1
    
    var body: some View {
        ZStack {
            
            TabView(selection: $count) {
                Rectangle()
                    .foregroundColor(.red)
                    .overlay(Text("안").font(.largeTitle).foregroundColor(.white))
                    .tag(1)
                Rectangle()
                    .foregroundColor(.green)
                    .overlay(Text("녕").font(.largeTitle).foregroundColor(.white))
                    .tag(2)
                Rectangle()
                    .foregroundColor(.blue)
                    .overlay(Text("하").font(.largeTitle).foregroundColor(.white))
                    .tag(3)
                Rectangle()
                    .foregroundColor(.orange)
                    .overlay(Text("세").font(.largeTitle).foregroundColor(.white))
                    .tag(4)
                Rectangle()
                    .foregroundColor(.mint)
                    .overlay(Text("요").font(.largeTitle).foregroundColor(.white))
                    .tag(5)
            }
            .tabViewStyle(.page)
            .frame(height: 200)
            
        }
        .onReceive(timer) { value in
            
            count = count == 5 ? 1 : count + 1
        }
    }
}

 

 

원하는 간격에 따라 특정한 작업을 수행할 때 Timer를 활용할 수 있다! 정도로 요약 할 수 있겠다.