我最近发现,当动画永远运行并通过View
切换TabView
时,动画状态会丢失。虽然这是预期的行为,但我想了解在TabView视图更改后,是否有一种方法可以使动画在返回动画视图时保持运行?
这是一个简单的示例,展示了SwiftUI中的行为:
import SwiftUI
class MyState: ObservableObject {
@Published var animate: Bool = false
}
struct ContentView: View {
var body: some View {
TabView {
AnimationView()
.tabItem {
Image(systemName: "eye")
}
Text("Some View")
.tabItem {
Image(systemName: "play")
}
}
}
}
struct AnimationView: View {
@EnvironmentObject var myState: MyState
var body: some View {
VStack {
Text(self.myState.animate ? "Animation running" : "Animation stopped")
.padding()
ZStack {
Circle()
.stroke(style: StrokeStyle(lineWidth: 12.0))
.foregroundColor(Color.black)
.opacity(0.3)
Circle()
.trim(
from: 0,
to: self.myState.animate ? 1.0 : 0.0
)
.stroke(
style: StrokeStyle(lineWidth: 12.0, lineCap: .round, lineJoin: .round)
)
.animation(
Animation
.linear(duration: self.myState.animate ? 1.0 : 0.0)
.repeatForever(autoreverses: false)
)
}.frame(width: 200, height: 200)
Button(action: {
self.myState.animate.toggle()
}) {
Text("Toggle animation")
.padding()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(MyState())
}
}
在下面您可以看到该行为:
我希望控制的是回到动画运行所在的视图,并查看当前时间的状态-这是想象一下,当切换视图并返回到视图在“运行时间”或“实时”的正确位置找到它。
obs:状态是在MyState
和EnvironmentObject
中处理的,并且在视图中不是本地(@State)
答案 0 :(得分:2)
因此,我在您的MyState
上添加了一个额外的参数,以在用户在标签之间切换时存储当前的动画状态。下面是更新的代码。我还在代码中添加了注释,以更好地理解。
class MyState: ObservableObject {
@Published var animate: Bool = false
var isAnimationActive = false // Store animate status when user switches between tabs
}
struct AnimationView: View {
@EnvironmentObject var myState: MyState
var body: some View {
VStack {
Text(self.myState.animate ? "Animation running" : "Animation stopped")
.padding()
ZStack {
Circle()
.stroke(style: StrokeStyle(lineWidth: 12.0))
.foregroundColor(Color.black)
.opacity(0.3)
Circle()
.trim(
from: 0,
to: self.myState.animate ? 1.0 : 0.0
)
.stroke(
style: StrokeStyle(lineWidth: 12.0, lineCap: .round, lineJoin: .round)
)
.animation(
Animation
.linear(duration: self.myState.animate ? 1.0 : 0.0)
.repeatForever(autoreverses: false)
)
}.frame(width: 200, height: 200)
Button(action: {
self.myState.animate.toggle()
}) {
Text("Toggle animation")
.padding()
}
}
.onAppear(perform: {
if self.myState.isAnimationActive {
/*switching the animation inside withAnimation block is important*/
withAnimation {
self.myState.animate = true
}
}
})
.onDisappear {
self.myState.isAnimationActive = self.myState.animate
/*The property needs to be set to false as SwiftUI compare the
views before re-rendering them*/
self.myState.animate = false
}
}
}
答案 1 :(得分:1)
经过一些测试,到目前为止,我发现最好的解决方案是遵循以下规则。请记住,这仅在您可以及时跟踪确切的动画位置时才有用。因此,请检查以下流程:
1)停止动画.onDisappear
原因是具有动画元素的视图之间的状态更改将导致意外行为,例如,元素位置完全关闭等等。
self.myState.animate = false
2)跟踪进度
我的用例是一个很长的计算,从0开始到X结束,MyState
的方法为我提供了current position
的计算方法。然后,该值的计算范围为* 0到1,因此可以将其放入SwiftUI Animation
中。
3)快速启动到特定时间
SwiftUI Animation尚不支持此部分,但假设您有一个动画需要5秒钟,您的播放时间为50%,所以您将跳到要开始播放的2.5秒
为此的实现,如果您以前曾经使用过Java脚本,那么ActionScript是seek
(https://greensock.com/docs/v2/Core/Animation/seek())的GSAP文档
4)重新触发动画
我们首先通过更改状态来停止动画,让SwiftUI启动动画,在此阶段我们要做的就是再次更改状态
self.myState.animate = true
也许还有其他方法可以使动画继续运行,但是在撰写本文时还没有发现动画的记录,因此需要“从任何位置开始动画”。