【问题标题】:How to stop animation in the current state in SwiftUI如何在 SwiftUI 中停止当前状态下的动画
【发布时间】:2021-08-03 03:52:00
【问题描述】:

您好,我编写了一个简单的代码,在其中创建了一个动画,其中一个圆圈会改变其位置。当我点击那个圆圈时,我希望动画停止,但无论我将动画设置为 nil,它都不会停止。也许我的方法在某些方面是错误的。如果有人帮忙,我会很高兴。

struct ContentView: View {
    @State private var enabled = true
    @State private var offset  = 0.0
    
    var body: some View {

        Circle()
            .frame(width: 100, height: 100)
            .offset(y: CGFloat(self.offset))
            .onAppear {
                withAnimation(self.enabled ? .easeIn(duration: 5) : nil) {
                    self.offset = Double(-UIScreen.main.bounds.size.height + 300)
                }
            }
            .onTapGesture {
                self.enabled = false
            }

    }
}

【问题讨论】:

    标签: swift swiftui


    【解决方案1】:

    正如上面的@Schottky 所说,offset 的值已经设置好了,SwiftUI 只是对更改进行动画处理。

    问题在于,当您点击圆圈时,不会再次调用 onAppear,这意味着根本不会检查 enabled,即使是这样,它也不会阻止 offset 的动画。

    为了解决这个问题,我们可以引入Timer 并进行一些小计算,iOS 带有一个内置的Timer 类,可以让我们定期运行代码。所以代码看起来像这样:

    struct ContentView: View {
        @State private var offset  = 0.0
        // 1
        let timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
        @State var animationDuration: Double = 5.0
        var body: some View {
            Circle()
                .frame(width: 100, height: 100)
                .offset(y: CGFloat(self.offset))
                .onTapGesture {
                    // 2
                    timer.upstream.connect().cancel()
                }
                .onReceive(timer) { _ in
                    // 3
                    animationDuration -= 0.1
                    if animationDuration <= 0.0 {
                        timer.upstream.connect().cancel()
                    } else {
                        withAnimation(.easeIn) {
                            self.offset += Double(-UIScreen.main.bounds.size.height + 300)/50
                        }
                    }
                }
        }
    }
    
    1. 这将创建一个计时器发布者,它要求计时器每0.1 秒触发一次。它说计时器应该在main 线程上运行,它应该在common 运行循环上运行,这是你大部分时间想要使用的循环。 (运行循环让iOS 在用户主动做某事时处理正在运行的代码,例如滚动列表或点击按钮。)

    2. 当您点击圆圈时,计时器会自动取消。

    3. 我们需要使用名为onReceive() 的新修饰符手动捕捉公告(出版物)。它接受一个发布者作为它的第一个参数(这里是我们的计时器)和一个作为第二个参数运行的函数,它会确保每当发布者发送其更改通知时调用该函数(在这种情况下,每 0.1 秒) .

    每 0.1 秒后,我们减少动画的持续时间,当持续时间结束时(持续时间 = 0.0),我们停止计时器,否则我们将继续减少 offset

    查看triggering-events-repeatedly-using-a-timer 了解更多关于Timer的信息

    【讨论】:

    • 非常感谢您的清晰解释和出色的回答!
    【解决方案2】:

    您需要了解的是,只有已经发生的事情才会被动画化。只是需要更长的时间来展示。因此,换句话说,当您将偏移设置为某个值时,无论如何它都会朝着该值设置动画。因此,您需要做的是在点击圆时将偏移设置为零。动画系统足够智能,可以处理“冲突”动画并取消前一个。

    另外,一个快速提示:您可以使用var offset: CGFloat = 0.0,这样您就不必在 onAppear 修饰符中使用它

    【讨论】:

    • 感谢您的快速回答,但正如我所说,我想在当前状态下停止动画。在我的情况下,我试图让动画停止并保持圆圈位置,这是在点击的那一刻
    • 哦,我明白了。在这种情况下,您可能不应该查看 SwiftUI,因为它通常是为日历或邮件等应用程序设计的。看看developer.apple.com/documentation/spritekit也许对你有帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-23
    • 1970-01-01
    • 1970-01-01
    • 2022-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多