Skip to content

Instantly share code, notes, and snippets.

@emrepun
Created October 3, 2024 08:40
Show Gist options
  • Save emrepun/07246156ba8220aafd8dffbe66db3791 to your computer and use it in GitHub Desktop.
Save emrepun/07246156ba8220aafd8dffbe66db3791 to your computer and use it in GitHub Desktop.
Precise Timer Example in SwiftUI
import SwiftUI
struct ContentView: View {
@State var startDate: Date?
var body: some View {
TimelineView(MetricsTimelineSchedule(from: .now)) { _ in
VStack(spacing: 16.0) {
ElapsedTimeView(elapsedTime: elapsedTime())
HStack(spacing: 16.0) {
Button {
startDate = .now
} label: {
Text("Start")
}
Button {
startDate = nil
} label: {
Text("Stop")
.foregroundStyle(Color.red)
}
}
}
}
}
private func elapsedTime() -> TimeInterval {
guard let startDate else {
return 0
}
return -startDate.timeIntervalSinceNow
}
}
#Preview {
ContentView()
}
struct MetricsTimelineSchedule: TimelineSchedule {
var startDate: Date
init(from startDate: Date) {
self.startDate = startDate
}
func entries(from startDate: Date, mode: TimelineScheduleMode) -> AnyIterator<Date> {
var baseSchedule = PeriodicTimelineSchedule(
from: self.startDate,
by: (mode == .lowFrequency ? 1.0 : 1.0 / 30.0)
)
.entries(from: startDate, mode: mode)
return AnyIterator<Date> {
return baseSchedule.next()
}
}
}
struct ElapsedTimeView: View {
var elapsedTime: TimeInterval = 0
@State private var timeFormatter = ElapsedTimeFormatter()
var body: some View {
Text(NSNumber(value: elapsedTime), formatter: timeFormatter)
.font(.system(.largeTitle, design: .monospaced))
.foregroundStyle(Color.yellow)
}
}
final class ElapsedTimeFormatter: Formatter {
let componentsFormatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.minute, .second]
formatter.zeroFormattingBehavior = .pad
return formatter
}()
override func string(for value: Any?) -> String? {
guard let time = value as? TimeInterval else {
return nil
}
guard let formattedString = componentsFormatter.string(from: time) else {
return nil
}
let hundredths = Int((time.truncatingRemainder(dividingBy: 1)) * 100)
let decimalSeparator = Locale.current.decimalSeparator ?? "."
return String(format: "%@%@%0.2d", formattedString, decimalSeparator, hundredths)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment