diff options
| author | Dustin Mierau <dustin@mierau.me> | 2025-11-07 12:44:55 -0800 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2025-11-07 12:44:55 -0800 |
| commit | a55318fa8d643160900bec3e6b14e7404c63497a (patch) | |
| tree | ab6a9eca9a368b35e1490becc70f94ebde6ac271 /Hotline/Library/Views/SpinningGlobeView.swift | |
| parent | 786a387d58fc66ae20716a9dee46b74dff8e6894 (diff) | |
Organize Library folder a bit. Update Tracker add/edit sheet design.
Diffstat (limited to 'Hotline/Library/Views/SpinningGlobeView.swift')
| -rw-r--r-- | Hotline/Library/Views/SpinningGlobeView.swift | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/Hotline/Library/Views/SpinningGlobeView.swift b/Hotline/Library/Views/SpinningGlobeView.swift new file mode 100644 index 0000000..bccb969 --- /dev/null +++ b/Hotline/Library/Views/SpinningGlobeView.swift @@ -0,0 +1,90 @@ +import SwiftUI + +/// An animated globe icon that cycles through different world regions +/// +/// Displays a spinning globe effect by cycling through SF Symbol images showing +/// different parts of the world. Useful for indicating network activity or global content. +/// +/// Example: +/// ```swift +/// SpinningGlobeView() +/// .frame(width: 16, height: 16) +/// +/// SpinningGlobeView(frameDelay: 0.5) +/// .frame(width: 24, height: 24) +/// ``` +struct SpinningGlobeView: View { + /// Delay between frames in seconds (default: 0.3) + let frameDelay: TimeInterval + + /// SF Symbol names for each frame of the globe animation + private let globeFrames = [ + "globe.americas.fill", + "globe.europe.africa.fill", + "globe.central.south.asia.fill", + "globe.asia.australia.fill" + ] + + @State private var currentFrameIndex = 0 + @State private var animationTask: Task<Void, Never>? + + init(frameDelay: TimeInterval = 0.25) { + self.frameDelay = frameDelay + } + + var body: some View { + Image(systemName: globeFrames[currentFrameIndex]) + .onAppear { + startAnimation() + } + .onDisappear { + stopAnimation() + } + } + + private func startAnimation() { + animationTask = Task { + while !Task.isCancelled { + try? await Task.sleep(nanoseconds: UInt64(frameDelay * 1_000_000_000)) + + guard !Task.isCancelled else { break } + + currentFrameIndex = (currentFrameIndex + 1) % globeFrames.count + } + } + } + + private func stopAnimation() { + animationTask?.cancel() + animationTask = nil + } +} + +#Preview { + VStack(spacing: 20) { + HStack(spacing: 20) { + SpinningGlobeView() + .frame(width: 12, height: 12) + + SpinningGlobeView() + .frame(width: 16, height: 16) + + SpinningGlobeView() + .frame(width: 24, height: 24) + } + + Text("Different frame delays:") + + HStack(spacing: 20) { + SpinningGlobeView(frameDelay: 0.1) + .frame(width: 16, height: 16) + + SpinningGlobeView(frameDelay: 0.3) + .frame(width: 16, height: 16) + + SpinningGlobeView(frameDelay: 0.6) + .frame(width: 16, height: 16) + } + } + .padding() +} |