在 SwiftUI 中链接动画

Posted

技术标签:

【中文标题】在 SwiftUI 中链接动画【英文标题】:Chaining animations in SwiftUI 【发布时间】:2019-07-05 17:44:19 【问题描述】:

我正在 SwiftUI 中制作一个相对复杂的动画,我想知道链接各个动画阶段的最佳/最优雅的方式是什么。

假设我有一个首先需要缩放的视图,然后等待几秒钟然后淡出(然后等待几秒钟并重新开始 - 无限期地)。

如果我尝试在同一个视图/堆栈上使用多个 withAnimation() 块,它们最终会相互干扰并弄乱动画。

到目前为止,我能想到的最好的方法是在初始视图 .onAppear() 修饰符上调用自定义函数,并在该函数中为动画的每个阶段设置 withAnimation() 块,并在它们之间设置延迟。所以,它基本上看起来像这样:

func doAnimations() 
  withAnimation(...)

  DispatchQueue.main.asyncAfter(...)
    withAnimation(...)

  DispatchQueue.main.asyncAfter(...)
    withAnimation(...)

  ...


它最终会变得很长而且不是很“漂亮”。我确信必须有更好/更好的方法来做到这一点,但到目前为止我尝试的一切都没有给我想要的确切流程。

任何想法/建议/提示将不胜感激。谢谢!

【问题讨论】:

【参考方案1】:

正如其他回复中提到的,目前 SwiftUI 中没有链接动画的机制,但您不一定需要使用手动计时器。相反,您可以在链式动画上使用delay 函数:

withAnimation(Animation.easeIn(duration: 1.23)) 
    self.doSomethingFirst()


withAnimation(Animation.easeOut(duration: 4.56).delay(1.23)) 
    self.thenDoSomethingElse()


withAnimation(Animation.default.delay(1.23 + 4.56)) 
    self.andThenDoAThirdThing()


我发现与使用 DispatchQueueTimer 相比,这会导致更一致地更平滑的链接动画,这可能是因为它对所有动画使用相同的调度程序。

同时处理所有延迟和持续时间可能很麻烦,因此雄心勃勃的开发人员可能会将计算抽象到某个全局 withChainedAnimation 函数中,而不是为您处理。

【讨论】:

【参考方案2】:

使用计时器有效。这是我自己的项目:

@State private var isShowing = true
@State private var timer: Timer?

...

func askQuestion() 
    withAnimation(Animation.easeInOut(duration: 1).delay(0.5)) 
        isShowing.toggle()
    
    timer = Timer.scheduledTimer(withTimeInterval: 1.6, repeats: false)  _ in
        withAnimation(.easeInOut(duration: 1)) 
            self.isShowing.toggle()
        
        self.timer?.invalidate()
    

    // code here executes before the timer is triggered.


【讨论】:

【参考方案3】:

恐怕暂时不支持关键帧之类的东西。至少他们可以添加onAnimationEnd()...但是没有这样的东西。

我确实有一些运气,是动画形状路径。虽然没有关键帧,但您拥有更多控制权,因为您可以定义“AnimatableData”。例如,查看我对另一个问题的回答:https://***.com/a/56885066/7786555

在这种情况下,它基本上是一个旋转的弧线,但会从零增长到一定长度,并在转弯结束时逐渐回到零长度。动画有 3 个阶段:一开始,弧的一端移动,但另一端不移动。然后他们都以相同的速度一起移动,最后第二端到达第一端。我的第一种方法是使用 DispatchQueue 的想法,它奏效了,但我同意:它非常丑陋。然后我想知道如何正确使用 AnimatableData。所以......如果你正在为路径设置动画,那么你很幸运。否则,我们似乎不得不等待更优雅的代码的可能性。

【讨论】:

感谢您的快速回复。让我问你这个问题 - 如果你需要从另一个问题中获取动画,请在动画完成一个完整的循环时停止动画,然后淡出并重新开始动画。你能想出一种不添加 DispatchQueue 的方法吗? 从我的脑海中,我想不出办法:-(我确实尝试同时设置两个动画,其中一个使用 animation().delay() 所以它会从第一个完成时开始。但我放弃了这个想法,因为我不是很成功。但也许这是你可以探索的东西。 是的,我也尝试过,但运气不佳... :[再次感谢,祝我们大家好运... :]

以上是关于在 SwiftUI 中链接动画的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI onAppear:动画持续时间被忽略

如何在 SwiftUI 的 ScrollView 中制作动画? SwiftUI 中的手风琴风格动画

SwiftUI 添加自定义 UIViewControllerTransitioningDelegate

如何阻止视图在 SwiftUI 中出现动画?

如何在 SwiftUI 中暂停动画?

SwiftUI:删除列表中的动画?