Swift 如何保持动画在TabView视图更改上运行?
我最近发现,当动画永远运行,并且通过Swift 如何保持动画在TabView视图更改上运行?,swift,swiftui,Swift,Swiftui,我最近发现,当动画永远运行,并且通过选项卡视图切换视图时,动画状态将丢失。虽然这是预期的行为,但我想了解在TabView更改后,是否有办法在返回动画视图时保持动画运行 下面是一个快速示例,展示了SwiftUI中的行为: import SwiftUI class MyState: ObservableObject { @Published var animate: Bool = false } struct ContentView: View { var body: some
选项卡视图切换视图时,动画状态将丢失。虽然这是预期的行为,但我想了解在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:State在MyState
一个EnvironmentObject
中处理,并且它不是视图中的本地(@State),所以我在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
}
}
}
因此,我在您的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) 停止动画.onDisappear
原因是,具有动画元素的视图之间的状态更改将导致意外行为,例如,元素定位完全关闭等等
self.myState.animate = false
2) 跟踪进度
我的用例是一个从0开始到X结束的长计算,MyState
的方法为我提供了计算中的当前位置。然后在*0到1的范围内计算该值,以便可以将其放入SwiftUI动画中
3) 跳转到特定时间
这一部分在SwiftUI动画中还不受支持,但假设你有一个需要5秒的动画,你有50%的播放时间,所以你可以跳到2.5秒开始播放
如果您以前体验过Javascript和way,那么ActionScript就是GSAPdoc forseek
()
4) 重新触发动画
我们首先通过改变状态来停止动画,为了让SwiftUI启动动画,我们在这个阶段需要做的就是再次改变状态
self.myState.animate = true
可能还有其他方法来保持动画运行,但在撰写本文时,还没有发现它的文档,因此需要“从任何点开始动画”。经过一些测试后,我发现目前为止最好的解决方案是遵循我在下面陈述的一些规则。请记住,只有当您能够及时跟踪精确的动画位置时,这才有用。因此,请检查以下流程:
1) 停止动画.onDisappear
原因是,具有动画元素的视图之间的状态更改将导致意外行为,例如,元素定位完全关闭等等
self.myState.animate = false
2) 跟踪进度
我的用例是一个从0开始到X结束的长计算,MyState
的方法为我提供了计算中的当前位置。然后在*0到1的范围内计算该值,以便可以将其放入SwiftUI动画中
3) 跳转到特定时间
这一部分在SwiftUI动画中还不受支持,但假设你有一个需要5秒的动画,你有50%的播放时间,所以你可以跳到2.5秒开始播放
如果您以前体验过Javascript和way,那么ActionScript就是GSAPdoc forseek
()
4) 重新触发动画
我们首先通过改变状态来停止动画,为了让SwiftUI启动动画,我们在这个阶段需要做的就是再次改变状态
self.myState.animate = true
可能还有其他方法来保持动画的运行,但在撰写本文时还没有发现它的文档,因此需要“从任何点开始动画”