👔TimerView
// 2022.03.24: refactored to be more flexible
// ------------------------------------------------------
// ⭐️ 潛藏問題:
// 如果同時使用兩個 TimerView,timer 竟然不是各自獨立❓
// ( 👉 參看:「👁️ 預覽」)
// ------------------------------------------------------
import SwiftUI
import Combine
/// 👔 TimerView
/// ```
/// TimerView(every: 1) { ... }
/// TimerView(every: 1, update: { time in ... }) { ... }
/// ```
struct TimerView<Content: View>: View {
// ⭐️ time interval on which to publish events
var interval: TimeInterval = 1
// ⭐️ update with time
var update: (Date) -> Void = { _ in }
// ⭐️ generate timer content
@ViewBuilder var content: () -> Content
// ⭐️ timer that fires on the main thread.
let timer: Publishers.Autoconnect<Timer.TimerPublisher>
// init
init(
every interval: TimeInterval,
update: @escaping (Date) -> Void = {_ in}, // default: do nothing
@ViewBuilder content: @escaping () -> Content
) {
self.content = content
self.update = update
self.timer = Timer
.publish(every: interval, on: .main, in: .common)
.autoconnect() // ⭐️ auto-connect when subscribed
}
// ⭐️ detect whether app has gone background
@Environment(\.scenePhase) private var scenePhase
@State private var isActive = true
var body: some View {
content()
// ⭐️ whenever `timer` fires, update with time
.onReceive(timer) { time in
// ⭐️ if app goes background, stop updating immediately.
guard isActive else { return }
// ⭐️ update with time
update(time)
}
// ⭐️ mark the app inactive once it goes background.
.onChange(of: scenePhase) { newPhase in
isActive = (newPhase == .active)
}
}
}
History
2022.03.24
Last updated
Was this helpful?