Created
October 3, 2024 08:40
-
-
Save emrepun/07246156ba8220aafd8dffbe66db3791 to your computer and use it in GitHub Desktop.
Precise Timer Example in SwiftUI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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