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

Swift中如何实现数字滚动动画?(步数统计场景)

问题描述

我想实现数字从0快速递增到当前数值的动画效果,这个数值是用户的步数。现有SwiftUI代码通过LazyVStack展示步数:

LazyVStack{
    ForEach(steps, id: \.id) { step in
        // 这里是需要加动画的数字
        Text("\(step.count)")
            .font(.custom(customFont, size: 50))
        
        Text("Steps")
            .font(.custom(customFont, size: 25))
            .multilineTextAlignment(.center)
    }
}

我已经写了一个动画函数,需要把它应用到上面的步数文本上:

func addNumberWithRollingAnimation() {
    withAnimation {
        // 确定动画任务数
        let animationDuration = 1000 // 毫秒
        let tasks = min(abs(self.enteredNumber), 100)
        let taskDuration = (animationDuration / tasks)
        
        // 先加上余数部分
        total += self.enteredNumber % tasks
        // 遍历每个任务
        (0..<tasks).forEach { task in
            // 设置更新时间
            let updateTimeInterval = DispatchTimeInterval.milliseconds(task * taskDuration)
            let deadline = DispatchTime.now() + updateTimeInterval
            
            // 到时间后更新数值
            DispatchQueue.main.asyncAfter(deadline: deadline) {
                self.total += Int(self.enteredNumber / tasks)
            }
        }
    }
}

请问怎么把这个函数应用到步数文本上,实现数字滚动动画?

解决方案

1. 调整数据模型,新增动画用动态属性

你的steps数组元素(假设是Step结构体)需要添加一个用于动画的动态数值属性,用来逐步递增到目标步数:

struct Step: Identifiable {
    let id = UUID()
    let count: Int // 最终目标步数
    var animatedCount: Int = 0 // 动画过程中实时更新的数值
}

2. 适配动画函数,支持单个Step的动画

原函数是针对全局数值的,现在修改为可以处理单个Step的版本,传入目标数值和动态数值的引用:

func animateStepCount(to target: Int, animatedCount: inout Int) {
    let animationDuration = 1000 // 动画总时长(毫秒)
    let tasks = min(abs(target), 100) // 拆分动画步骤,最多100次避免刷新过于频繁
    guard tasks > 0 else { return }
    
    let increment = target / tasks
    let remainder = target % tasks
    
    // 重置动画起始值为0
    animatedCount = 0
    
    // 先补上余数部分,保证最终数值准确
    DispatchQueue.main.asyncAfter(deadline: .now()) {
        animatedCount += remainder
    }
    
    // 分批递增数值,实现滚动效果
    (0..<tasks).forEach { task in
        let interval = DispatchTimeInterval.milliseconds(task * (animationDuration / tasks))
        DispatchQueue.main.asyncAfter(deadline: .now() + interval) {
            animatedCount += increment
        }
    }
}

3. 在视图中绑定动态数值并触发动画

在LazyVStack的ForEach中使用绑定来访问step.animatedCount,并在视图出现时启动动画:

LazyVStack{
    ForEach($steps) { $step in // 使用绑定类型,让视图能响应animatedCount的变化
        Text("\(step.animatedCount)")
            .font(.custom(customFont, size: 50))
            .onAppear {
                // 视图加载完成后,启动从0到目标步数的滚动动画
                animateStepCount(to: step.count, animatedCount: &step.animatedCount)
            }
        
        Text("Steps")
            .font(.custom(customFont, size: 25))
            .multilineTextAlignment(.center)
    }
}

关键注意事项

  • 确保steps数组由@State、@ObservedObject或@EnvironmentObject管理,这样修改animatedCount时视图才会自动刷新。
  • 如果后续步数有更新,只需再次调用animateStepCount函数,传入新的目标数值即可重新触发动画。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 13:57:19