SwiftUI 动画和随后的反向动画到原始状态

Posted

技术标签:

【中文标题】SwiftUI 动画和随后的反向动画到原始状态【英文标题】:SwiftUI animation and subsequent reverse animation to original state 【发布时间】:2020-05-23 12:17:48 【问题描述】:

我正在使用 SwiftUI,并且我想在视图出现后立即为其设置动画(动画的显式类型无关紧要),以便在我的应用程序中进行演示。 假设我只想放大我的视图,然后再次将其缩小到原来的大小,我需要能够将视图设置为新状态并在之后立即恢复到原始状态。 这是我到目前为止尝试过的示例代码:

import SwiftUI
import Combine

struct ContentView: View 
    @State private var shouldAnimate = false
    private var scalingFactor: CGFloat = 2    

    var body: some View 
        Text("hello world")
        .scaleEffect(self.shouldAnimate ? self.scalingFactor : 1)
        .onAppear 
            let animation = Animation.spring().repeatCount(1, autoreverses: true)
            withAnimation(animation) 
                self.shouldAnimate.toggle()
            
        
    

显然这并不能完全满足我的要求,因为let animation = Animation.spring().repeatCount(1, autoreverses: true) 仅通过使用平滑的autoreverse = true 设置来确保动画(到新状态)正在重复,这仍然会导致视图处于最终状态缩放到scalingFactor

所以我在animation 上也找不到任何属性,它可以让我的动画自动恢复到原始状态(在第一个动画之后我不必与视图交互),我也没有找到任何关于如何确定第一个动画何时真正完成,以便能够触发新动画。

我发现在某些视图的外观上制作动画是很常见的做法,例如只是为了展示这个视图可以交互,但最终不会改变视图的状态。例如,在按钮上设置弹跳效果动画,最终将按钮设置回其原始状态。当然,我找到了几个解决方案,建议与按钮交互以触发反向动画回到其原始状态,但这不是我想要的。

【问题讨论】:

【参考方案1】:

这是一个基于ReversingScale 动画修饰符的解决方案,来自this my answer

使用 Xcode 11.4 / ios 13.4 测试

struct DemoReverseAnimation: View 
    @State var scalingFactor: CGFloat = 1

    var body: some View 
        Text("hello world")
        .modifier(ReversingScale(to: scalingFactor, onEnded: 
            DispatchQueue.main.async 
                self.scalingFactor = 1
            
        ))
        .animation(.default)
        .onAppear 
            self.scalingFactor = 2
        
    

【讨论】:

在此基础上:如果想要平滑的反向动画,我们可以将 self.scalingFactor = 1 包裹在 withAnimation 块中:DispatchQueue.main.async withAnimation self.scalingFactor = 1 【参考方案2】:

如果您定义动画应该花费多长时间,另一种可行的方法:

struct ContentView: View 
    @State private var shouldAnimate = false
    private var scalingFactor: CGFloat = 2

    var body: some View 
        Text("hello world")
            .scaleEffect(self.shouldAnimate ? self.scalingFactor : 1)
            .onAppear 
                let animation = Animation.easeInOut(duration: 2).repeatCount(1, autoreverses: true)
                withAnimation(animation) 
                    self.shouldAnimate.toggle()
                
                DispatchQueue.main.asyncAfter(deadline: .now() + 2) 
                    withAnimation(animation) 
                        self.shouldAnimate.toggle()
                    
                
        
    

【讨论】:

这么好的解决方案。非常感谢。

以上是关于SwiftUI 动画和随后的反向动画到原始状态的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 反向动画延迟移除

动画不适用于 SwiftUI 视图状态更改

SwiftUI 中的动画状态变化

在 SwiftUI 中动态更改动画的持续时间

一行代码为特定状态绑定SwiftUI视图动画

一行代码为特定状态绑定SwiftUI视图动画