如何制作一个简单的缩放动画以及为什么这不起作用?

Chr*_*ris 2 animation swiftui

我刚刚在 stackoverflow 中读到,我只能将动画与延迟连接起来,所以我在这里尝试了这个,它只是缩小然后再次缩放圆圈。不幸的是,缩小不起作用!?如果我注释掉不断增长、不断缩小的作品……

struct ContentView: View {
    
    @State var scaleImage : CGFloat = 1
    
    var body: some View {
        VStack {
            Button(action: {
                withAnimation(Animation.easeInOut(duration: 1)) {
                    self.scaleImage = 0.01
                }
                
                withAnimation(Animation.easeInOut(duration: 1).delay(1.0)) {
                    self.scaleImage = 1
                }
            }) {
                Text ("Start animation")
            }
            Image(systemName: "circle.fill")
                .scaleEffect(scaleImage)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Asp*_*eri 8

这是可能的方法(基于AnimatableModifier)。实际上,它演示了如何检测当前动画结束并执行某些操作 - 在这种情况下,对于您的缩放场景,只需启动反转即可。

演示

简化并修改了您的示例

struct TestReversingScaleAnimation: View {

    @State var scaleImage : CGFloat = 1

    var body: some View {
        VStack {
            Button("Start animation") {
                self.scaleImage = 0.01       // initiate animation
            }

            Image(systemName: "circle.fill")
                .modifier(ReversingScale(to: scaleImage) {
                    self.scaleImage = 1      // reverse set
                })
                .animation(.default)         // now can be implicit
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

事实上,这里的节目制作人...重要的内嵌评论。

针对 Xcode 13.3 进行了更新(使用 iOS 15.4 进行测试)

struct ReversingScale: AnimatableModifier {
    var value: CGFloat

    private let target: CGFloat
    private let onEnded: () -> ()

    init(to value: CGFloat, onEnded: @escaping () -> () = {}) {
        self.target = value
        self.value = value
        self.onEnded = onEnded // << callback
    }

    var animatableData: CGFloat {
        get { value }
        set { value = newValue
            // newValue here is interpolating by engine, so changing
            // from previous to initially set, so when they got equal
            // animation ended
            let callback = onEnded
            if newValue == target {
                DispatchQueue.main.async(execute: callback)
            }
        }
    }

    func body(content: Content) -> some View {
        content.scaleEffect(value)
    }
}
Run Code Online (Sandbox Code Playgroud)

原始变体(使用 Xcode 11.4 / iOS 13.4 测试)

struct ReversingScale: AnimatableModifier {
    var value: CGFloat

    private var target: CGFloat
    private var onEnded: () -> ()

    init(to value: CGFloat, onEnded: @escaping () -> () = {}) {
        self.target = value
        self.value = value
        self.onEnded = onEnded // << callback
    }

    var animatableData: CGFloat {
        get { value }
        set { value = newValue
            // newValue here is interpolating by engine, so changing
            // from previous to initially set, so when they got equal
            // animation ended
            if newValue == target {
                onEnded()
            }
        }
    }

    func body(content: Content) -> some View {
        content.scaleEffect(value)
    }
}
Run Code Online (Sandbox Code Playgroud)