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

SwiftUI数字计数动画异常:卡顿不流畅问题排查

SwiftUI数字计数动画卡顿修复方案

问题分析

你实现的AnimatableNumber组件出现卡顿(比如停在中间值直接跳转到最大值),核心原因有三点:

  1. 使用CGFloat作为动画插值载体,精度不足导致数值跳变;
  2. easeInOut动画曲线在首尾阶段插值速度过慢,使得整数取整后长时间停留在同一数值,最终突然跳转;
  3. 未处理视图重新出现的情况,动画状态未重置。

修改后的完整代码

public struct AnimatableNumber: View {
    @State private var animatedValue: Double = 0

    let maxNumber: Double
    let font: SwiftUI.Font
    let animationDuration: Double

    public init(maxNumber: Double,
                font: SwiftUI.Font,
                animationDuration: Double = 2) {
        self.maxNumber = maxNumber
        self.font = font
        self.animationDuration = animationDuration
    }

    public var body: some View {
        // 用目标数值的文本固定视图尺寸,避免动画过程中布局变化
        GeometryReader { proxy in
            Text("\(Int(maxNumber))")
                .font(font)
                .foregroundColor(.clear)
                .modifier(AnimatingNumberOverlay(number: animatedValue, font: font))
                .minimumScaleFactor(0.5)
                .frame(width: proxy.size.width, height: proxy.size.height)
        }
        .fixedSize()
        .onAppear {
            // 重置后启动动画,确保视图重新出现时也能正常播放
            animatedValue = 0
            withAnimation(Animation.linear(duration: animationDuration)) {
                animatedValue = maxNumber
            }
        }
        .onDisappear {
            animatedValue = 0
        }
    }
}

private struct AnimatingNumberOverlay: AnimatableModifier {
    var number: Double = 0
    let font: SwiftUI.Font

    var animatableData: Double {
        get { number }
        set { number = newValue }
    }

    func body(content: Content) -> some View {
        content.overlay(
            Text(String(format: "%.0f", number))
                .font(font)
        )
    }
}

关键修改点说明

  • 改用Double类型:相比CGFloat拥有更高的精度,避免插值过程中出现数值丢失或跳变;
  • 线性动画曲线:使用Animation.linear替代easeInOut,保证数值匀速变化,消除首尾阶段的停顿问题;
  • 状态重置逻辑:在onDisappear中重置动画数值,确保视图重新出现时能从头开始播放动画;
  • 格式化数值显示:用String(format: "%.0f", number)替代直接Int(number)转换,取整逻辑更稳定;
  • 固定视图尺寸:通过GeometryReader和frame强制锁定视图大小,避免动画过程中布局变化干扰流畅度。

额外优化建议

如果目标数值极大(如10000+),固定时长的动画仍会出现较大的数值跳变,可根据数值动态调整动画时长:

// 在init中添加时长调整逻辑
let adjustedDuration: Double
public init(maxNumber: Double,
            font: SwiftUI.Font,
            baseDuration: Double = 2) {
    self.maxNumber = maxNumber
    self.font = font
    // 每100个数值占用基础时长,可根据需求调整比例
    self.adjustedDuration = baseDuration * (maxNumber / 100)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 17:27:16