# TimerView (scheduledTimer)

[Combine](..) ⟩ [Timer]() ⟩ TimerView (scheduledTimer)

{% tabs %}
{% tab title="💾 scheduledTimer" %}
````swift
// 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)
            }
    }
}
````
{% endtab %}

{% tab title="👁️ 預覽" %}
{% embed url="https://youtu.be/4JabCEdJkSE" %}

```swift
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()
    }
}
```
{% endtab %}

{% tab title="📗 參考" %}
* [x] Mastering SwiftUI, Ch.9: Animations, Progress Indicator (p.205)
{% endtab %}

{% tab title="👥 相關" %}
* compare: [timerview](timerview "mention") - use [Timer.publish()](https://developer.apple.com/documentation/foundation/timer/3329589-publish).
* compare: [loading-indicators](../../../../../../swiftui/anim/examples/loading-indicators "mention").
* do [anim](../../../../../../swiftui/anim "mention") with [](<> "mention").
{% endtab %}
{% endtabs %}
