SwiftUI .delay 动画时序离开再回来查看不正确
SwiftUI .delay animation timing is incorrect after leaving and coming back to view
此动画在第一次观看时按预期运行,但在应用程序退出然后进入时(或者如果您离开屏幕并返回),时间随机混乱。如何更改此代码以保持离开屏幕和返回后动画时间一致?
可以通过退出并返回应用几次来重现错误。
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
}
}
}
这里有一个适合你的方法,需要用到DispatchQueue和scenePhase:
PS:我注意到 Xcode 对您的原始代码有很多抱怨!我重构了您的代码并解决了那些抱怨的问题。
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)
}
}
此动画在第一次观看时按预期运行,但在应用程序退出然后进入时(或者如果您离开屏幕并返回),时间随机混乱。如何更改此代码以保持离开屏幕和返回后动画时间一致?
可以通过退出并返回应用几次来重现错误。
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
}
}
}
这里有一个适合你的方法,需要用到DispatchQueue和scenePhase:
PS:我注意到 Xcode 对您的原始代码有很多抱怨!我重构了您的代码并解决了那些抱怨的问题。
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)
}
}