👔TimerView (scheduledTimer)
Combine ⟩ Timer ⟩ TimerView (scheduledTimer)
// 2022.03.25: (*) use Timer.scheduledTimer()
import SwiftUI
import Combine
/// 👔 TimerView (Timer.scheduledTimer)
/// ```
/// TimerView { ... }
/// TimerView(every: 2) { ... }
/// TimerView(every: 3, update: { timer in ... }) { ... }
/// ```
struct TimerView<Content: View>: View {
    
    // ⭐️ # of seconds between firings of timer
    let interval: TimeInterval
    
    // ⭐️ timer event handler
    let update: (Timer) -> Void
    
    // ⭐️ generate timer content
    @ViewBuilder var content: () -> Content 
        
    // init
    init(
        every interval: TimeInterval = 1, 
        update: @escaping (Timer) -> Void = {_ in},     // default: do nothing
        @ViewBuilder content: @escaping () -> Content
    ) {
        self.interval = interval
        self.content = content
        self.update = update
    }
    
    // ⭐️ detect whether app has gone background
    @Environment(\.scenePhase) private var scenePhase
    @State private var isActive = true
    
    var body: some View {
        content()
            .onAppear {
                // ⭐️ scheduled timer
                Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { timer in 
                    // ⭐️ if app goes background, stop updating immediately.
                    guard isActive else { return }
                    update(timer)
                }
            }
            // ⭐️ mark the app inactive once it goes background.
            .onChange(of: scenePhase) { newPhase in
                isActive = (newPhase == .active)
            }
    }
}struct ContentView: View {
    @State private var progress: CGFloat = 0
    @State private var timeLeft = 5
    let duration: TimeInterval = 5
    
    var body: some View {
        HStack {
            // TimerView 1
            TimerView(
                every: 1, 
                update: { timer in 
                    if timeLeft > 0 { timeLeft -= 1 }
                    else { timer.invalidate() }
                }
            ) { 
                Text("\(timeLeft)")
                    .font(.system(.title3, design: .rounded))
                    .frame(width: 80, height:40)
                    .background(Capsule().fill(.pink))
            }
            .padding(.trailing, 20)
            
            // TimerView 2
            TimerView(
                every: duration/100, 
                update: { timer in 
                    if progress < 1 { progress += 0.01 }
                    else { timer.invalidate() }
                }
            ) { 
                ZStack {
                    Circle()
                        .stroke(Color(.systemGray5), lineWidth: 14)
                    Circle()
                        .trim(from: 0, to: progress)
                        .stroke(.green, lineWidth: 12)
                        .rotationEffect(.degrees(-90))
                    Text("\(Int(progress * 100))%")
                        .font(.system(.title3, design: .rounded))
                }
            }
            .frame(100)
        }
        .padding()
    }
}- compare: TimerView - use Timer.publish(). 
- compare: Loading Indicators. 
- do animations with Timer. 
Last updated
Was this helpful?