Created
April 29, 2023 05:17
-
-
Save HarshilShah/3e8a5ac15c55d2b56aab5cd342ea2534 to your computer and use it in GitHub Desktop.
A SwiftUI view that performs a looping animation in whole cycles. When the animation is disabled, it drives the current loop to completion, and pauses only then
This file contains 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
struct CycledAnimationTimelineView<Content: View>: View { | |
var duration: Double | |
var isAnimating: Bool | |
@ViewBuilder var content: (Double) -> Content | |
@State private var isActuallyAnimating = false | |
@State private var startDate: Date? | |
var body: some View { | |
TimelineView(.animation(paused: !isActuallyAnimating)) { context in | |
content(animationProgress(at: context.date)) | |
} | |
.onChange(of: isAnimating) { isAnimating in | |
if isAnimating { | |
startAnimation() | |
} | |
} | |
.onAppear { | |
if isAnimating { | |
startAnimation() | |
} | |
} | |
} | |
func startAnimation() { | |
isActuallyAnimating = true | |
if startDate == nil { | |
startDate = .now | |
} | |
} | |
func animationProgress(at date: Date) -> Double { | |
guard isActuallyAnimating else { return 0 } | |
guard let startDate else { | |
DispatchQueue.main.async { startDate = date } | |
return 0 | |
} | |
let progress = (date.timeIntervalSince(startDate) / duration).truncatingRemainder(dividingBy: 1) | |
if !isAnimating, | |
progress.isApproximatelyEqual(to: 0, absoluteTolerance: 0.05, norm: \.magnitude) { | |
DispatchQueue.main.async { | |
isActuallyAnimating = false | |
self.startDate = nil | |
} | |
return .zero | |
} | |
return progress | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment