You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何仅当触发标识为true时触发SwiftUI动画?

如何仅在trigger为true时触发SwiftUI keyframe动画,而非状态变化即执行?

这个需求完全可以实现。默认的keyframeAnimator会在trigger状态每次变更时重启动画,我们可以通过添加一个一次性触发的状态标记,让动画仅在trigger从false切换到true时执行。

解决方案代码示例

struct AnimationValues: Animatable {
    var scale: Double = 1.0
    var animatableData: Double {
        get { scale }
        set { scale = newValue }
    }
}

struct ContentView: View {
    @State private var animationIsActive = false
    // 用于控制动画仅触发一次的标记
    @State private var shouldTriggerAnimation = false
    
    var body: some View {
        Label("Test", systemImage: "magnifyingglass")
            .keyframeAnimator(
                initialValue: AnimationValues(),
                trigger: shouldTriggerAnimation
            ) { content, frame in
                content
                    .scaleEffect(frame.scale)
            } keyframes: { _ in
                KeyframeTrack(\.scale) {
                    LinearKeyframe(0.5, duration: 0.1)
                    LinearKeyframe(1.5, duration: 0.1)
                    LinearKeyframe(1.0, duration: 0.1)
                }
            }
            .onChange(of: animationIsActive) { oldValue, newValue in
                // 仅当状态从false变为true时,激活动画触发标记
                if !oldValue && newValue {
                    shouldTriggerAnimation = true
                }
            }
            .onAnimationCompleted(for: shouldTriggerAnimation) {
                // 动画完成后重置标记,确保下次状态切换能再次触发
                shouldTriggerAnimation = false
            }
    }
}

// 扩展View,添加动画完成监听的便捷方法
extension View {
    func onAnimationCompleted<V: VectorArithmetic>(for value: V, completion: @escaping () -> Void) -> some View {
        self.modifier(AnimationCompletionModifier(observedValue: value, completion: completion))
    }
}

struct AnimationCompletionModifier<V: VectorArithmetic>: AnimatableModifier {
    var observedValue: V
    var completion: () -> Void
    
    var animatableData: V {
        didSet {
            checkIfAnimationCompleted()
        }
    }
    
    init(observedValue: V, completion: @escaping () -> Void) {
        self.observedValue = observedValue
        self.completion = completion
        self.animatableData = observedValue
    }
    
    func body(content: Content) -> some View {
        content
    }
    
    private func checkIfAnimationCompleted() {
        if animatableData == observedValue {
            DispatchQueue.main.async {
                completion()
            }
        }
    }
}

核心思路说明

  1. 新增触发标记:用shouldTriggerAnimation作为动画的实际trigger,而非直接使用animationIsActive
  2. 状态变化监听:通过onChange监听animationIsActive的切换,仅当它从false变为true时,才将shouldTriggerAnimation设为true以触发动画
  3. 重置触发标记:动画完成后,通过自定义的onAnimationCompleted方法重置shouldTriggerAnimation,确保下次animationIsActive再次变为true时,动画能重新触发

这样就能避免animationIsActive状态反复变化时重复触发动画,仅在需要的时机(从false到true的切换)执行一次动画。

内容的提问来源于stack exchange,提问作者GreatCornholio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 22:30:13