I want to finish the loading animation nicely when changing the refreshing
boolean in the following scenario:
public struct RefreshingView : View {
@State private var rotation = Angle(degrees: 0.0)
@Binding private var refreshing: Bool
public init(refreshing: Binding<Bool>) {
_refreshing = refreshing
}
public var body: some View {
if refreshing {
Image(systemName: "arrow.2.circlepath")
.foregroundColor(Color.red)
.rotationEffect(rotation)
.animateForever(using: .linear(duration: 1), autoreverses: false) {
rotation = Angle(degrees: -180)
}
} else {
Image(systemName: "arrow.2.circlepath")
.foregroundColor(Color.red)
.onAppear {
rotation = Angle(degrees: 0)
}
}
}
}
public extension View {
func animateForever(
using animation: Animation = Animation.easeInOut(duration: 1),
autoreverses: Bool = false,
_ action: @escaping () -> Void
) -> some View {
let repeated = animation.repeatForever(autoreverses: autoreverses)
return onAppear {
// That main.async is really important to change the animation from being explicit to implicit.
// https://stackoverflow.com/a/64566746/1979703
DispatchQueue.main.async {
withAnimation(repeated) {
action()
}
}
}
}
}
Currently, the problem is that when I change the refreshing
value the animation stops correctly, but it does not finsh the rotation so it looks like it is cut off. Is there a way to mitigate this by always finishing the current animation?