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

Posted

技术标签:

【中文标题】如何保持动画在 TabView 视图变化上运行?【英文标题】:How to keep the animation running on TabView View change? 【发布时间】:2020-05-26 20:03:25 【问题描述】:

我最近发现当一个动画永远运行并且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:状态在 MyStateEnvironmentObject 中处理,它不是视图中的本地 (@State)

【问题讨论】:

【参考方案1】:

所以我在你的MyState 中添加了一个额外的参数来存储用户在选项卡之间切换时的当前动画状态。以下是更新后的代码。我还在代码中添加了 cmets 以便更好地理解。

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
        
    

【讨论】:

【参考方案2】:

经过一些测试,到目前为止我发现的最佳解决方案是遵循我在下面陈述的一些规则。请记住,这仅在您可以及时跟踪确切的动画位置时才有用。所以请检查下面的过程:

1) 停止动画.onDisappear

原因是具有动画元素的视图之间的状态变化会导致意想不到的行为,例如,元素定位完全关闭等等。

self.myState.animate = false

2) 跟踪进度

我的用例是一个从 0 开始到 X 结束的长计算,MyState 具有在计算中为我提供current position 的方法。然后在* 0 到 1 的范围内计算此值,以便可以将其放入 SwiftUI Animation

3) 跳转到特定时间

SwiftUI Animation 尚不支持这部分,但假设您有一个需要 5 秒的动画,您的播放时间为 50%,所以您会跳到 2.5 秒开始播放.

对此的实现,如果您以前使用过 javascript,ActionScript 是 seek (https://greensock.com/docs/v2/Core/Animation/seek()) 的 GSAP 文档

4) 重新触发动画

我们首先通过改变状态来停止动画,为了让 SwiftUI 启动动画,我们在这个阶段需要做的就是再次改变状态

self.myState.animate = true

可能还有其他方法可以让动画保持运行,但在撰写本文时还没有发现它的文档,因此需要“从任意点开始动画”。

【讨论】:

以上是关于如何保持动画在 TabView 视图变化上运行?的主要内容,如果未能解决你的问题,请参考以下文章

如何保持不断变化的视图居中?

如何通过许多子视图保持 iOS 动画流畅

SwiftUI:输入字段键盘动画导致选项卡视图中的其他视图动画

使用 NavigationLink 打开新视图时如何隐藏 TabView?

Lottie 动画在 SwiftUI 的最后一个 TabView 页面上消失

TabView 在 iOS13 SwiftUI 上无法正常工作