如何保持动画在TabView视图上运行的变化?

时间:2020-05-26 20:03:25

标签: swift swiftui

我最近发现,当动画永远运行并通过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())
    }
}

在下面您可以看到该行为:

enter image description here

我希望控制的是回到动画运行所在的视图,并查看当前时间的状态-这是想象一下,当切换视图并返回到视图在“运行时间”或“实时”的正确位置找到它。

obs:状态是在MyStateEnvironmentObject中处理的,并且在视图中不是本地(@State)

2 个答案:

答案 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是seekhttps://greensock.com/docs/v2/Core/Animation/seek())的GSAP文档

4)重新触发动画

我们首先通过更改状态来停止动画,让SwiftUI启动动画,在此阶段我们要做的就是再次更改状态

self.myState.animate = true

也许还有其他方法可以使动画继续运行,但是在撰写本文时还没有发现动画的记录,因此需要“从任何位置开始动画”。