This animation runs as expected when first viewed but the timing gets messed up at random when the app is exited and then entered (or if you navigate away from the screen and back). How can this code be changed to keep the animation timing consistent after leaving the screen and coming back?
The error can be recreated by exiting and coming back to the app a couple times.
import SwiftUI
struct ExampleView: View {
@State var isAnimating: Bool = false
let timing = 4.0
let maxCounter: Int = 3
var body: some View {
ZStack {
Circle()
.stroke(
Color.blue.opacity(isAnimating ? 0.0 : 1.0),
style: StrokeStyle(lineWidth: isAnimating ? 0.0 : 15.0))
.scaleEffect(isAnimating ? 1.0 : 0.0)
.animation(
Animation.easeOut(duration: timing)
.repeatForever(autoreverses: false)
.delay(Double(0) * timing / Double(maxCounter) / Double(maxCounter)), value: isAnimating)
Circle()
.stroke(
Color.blue.opacity(isAnimating ? 0.0 : 1.0),
style: StrokeStyle(lineWidth: isAnimating ? 0.0 : 15.0))
.scaleEffect(isAnimating ? 1.0 : 0.0)
.animation(
Animation.easeOut(duration: timing)
.repeatForever(autoreverses: false)
.delay(Double(1) * timing / Double(maxCounter) / Double(maxCounter)), value: isAnimating)
Circle()
.stroke(
Color.blue.opacity(isAnimating ? 0.0 : 1.0),
style: StrokeStyle(lineWidth: isAnimating ? 0.0 : 15.0))
.scaleEffect(isAnimating ? 1.0 : 0.0)
.animation(
Animation.easeOut(duration: timing)
.repeatForever(autoreverses: false)
.delay(Double(2) * timing / Double(maxCounter) / Double(maxCounter)), value: isAnimating)
}
.frame(width: 200, height: 200, alignment: .center)
.onAppear {
isAnimating = true
}
}
}
CodePudding user response:
Here is a right way for you, it needs to use DispatchQueue and scenePhase:
PS: I noticed lots of complaining from Xcode with your original code! I refactored your code and solved those complaining as well.
struct ExampleView: View {
var body: some View {
ZStack {
CustomCircleAnimationView(delayValue: 0.0)
CustomCircleAnimationView(delayValue: 1.0)
CustomCircleAnimationView(delayValue: 2.0)
}
.frame(width: 200, height: 200)
}
}
struct CustomCircleAnimationView: View {
@Environment(\.scenePhase) private var scenePhase
let delay: Double
private let timing: Double
private let maxCounter: Double
@State private var startAnimation: Bool = false
init(timing: Double = 4.0, maxCounter: Double = 3.0, delayValue: Double) {
self.timing = timing
self.maxCounter = maxCounter
self.delay = (delayValue*timing)/(maxCounter*maxCounter)
}
var body: some View { if (scenePhase == .active) { circle } }
var circle: some View {
return Circle()
.stroke(Color.blue, style: StrokeStyle(lineWidth: startAnimation ? 0.001 : 15.0))
.scaleEffect(startAnimation ? 1.0 : 0.001)
.opacity(startAnimation ? 0.001 : 1.0)
.onAppear { DispatchQueue.main.async { startAnimation = true } }
.onDisappear { startAnimation = false }
.animation(Animation.easeOut(duration: timing).repeatForever(autoreverses: false).delay(delay), value: startAnimation)
}
}