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

SwiftUI中如何实现起始位置一致的原地动画?

复刻SwiftUI Symbol Bounce动画的通用方案

问题核心

常规的scaleEffect+弹簧动画方案会因状态切换逻辑导致视图停留在放大状态,而.symbolEffect(.bounce)是从初始状态出发、完成弹跳后自动回归初始状态的脉冲式动画,我们需要复刻这种行为。


方案一:通用自定义Modifier(推荐)

通过实现AnimatableModifier封装弹跳逻辑,支持任意视图,无需手动管理状态重置,动画结束后自动回归初始位置。

代码实现

import SwiftUI

struct BounceEffect: AnimatableModifier {
    var progress: Double = 0
    
    // 动画驱动的可插值数据
    var animatableData: Double {
        get { progress }
        set { progress = newValue }
    }
    
    func body(content: Content) -> some View {
        content
            .scaleEffect(bounceScale(for: progress))
    }
    
    // 模拟原生弹跳的缩放曲线
    private func bounceScale(for progress: Double) -> CGFloat {
        let normalizedProgress = min(max(progress, 0), 1)
        // 调整参数可改变弹跳强度、振荡次数:
        // - pow的系数控制衰减速度(值越大衰减越快)
        // - sin的周期控制弹跳频率(分母越小频率越高)
        let springCurve = pow(2, -12 * normalizedProgress) * sin((normalizedProgress - 0.06) * (2 * .pi) / 0.25) + 1
        return CGFloat(springCurve)
    }
}

// 给View扩展便捷调用方法
extension View {
    func bounceEffect(trigger: Bool) -> some View {
        self.modifier(BounceEffect(progress: trigger ? 1 : 0))
            .animation(.interpolatingSpring(stiffness: 400, damping: 15), value: trigger)
    }
}

使用方式

直接给任意视图添加该效果,触发布尔值切换即可启动动画:

@State private var triggerBounce = false

Button("触发弹跳") {
    triggerBounce.toggle()
}
.bounceEffect(trigger: triggerBounce)

方案二:withAnimation + 完成回调(简单场景)

针对iOS 17+,利用withAnimation的completion回调自动重置状态;旧版本则通过Task估算动画时长延迟重置,确保动画完成后回归初始位置。

代码实现

import SwiftUI

struct ContentView: View {
    @State private var bounce = false
    
    var body: some View {
        Button("触发弹跳") {
            if #available(iOS 17.0, *) {
                // iOS17+ 用completion回调自动重置
                withAnimation(.interpolatingSpring(stiffness: 380, damping: 12)) {
                    bounce = true
                } completion: {
                    bounce = false
                }
            } else {
                // iOS16及以下,估算动画时长延迟重置
                withAnimation(.interpolatingSpring(stiffness: 380, damping: 12)) {
                    bounce = true
                }
                Task {
                    try? await Task.sleep(for: .seconds(0.6)) // 匹配弹簧动画的总时长
                    bounce = false
                }
            }
        }
        .scaleEffect(bounce ? 1.2 : 1)
    }
}

效果优化建议

  • 方案一中的bounceScale函数可通过调整参数,完全匹配.symbolEffect(.bounce)的原生曲线;
  • 方案二中的弹簧参数(stiffness、damping)可根据需求微调:stiffness越大动画越"硬",damping越大回弹越快。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 23:03:20