ios – SwiftUI Rolling Text Animation Freezes with Rapid User Input


I’m working on a counter app in SwiftUI where I have implemented a rolling text animation for the numbers. The animation works fine when the number is incremented one at a time. However, when the user rapidly increases the number by multiple inputs in quick succession, the animation freezes momentarily before resuming.

Here’s the code for my RollingText view:

//
//  RollingText.swift
//

import SwiftUI

struct RollingText: View {
    var font: Font = .largeTitle
    var weight: Font.Weight = .regular

    @Binding var value: Int
    @State var animationRange: [Int] = []
    @State var isAnimating = false

    var body: some View {
        HStack(spacing: 0) {
            ForEach(0..<animationRange.count, id: \.self) { index in
                Text("8")
                    .font(font)
                    .fontWeight(weight)
                    .opacity(0)
                    .overlay {
                        GeometryReader { proxy in
                            let size = proxy.size

                            VStack(spacing: 0) {
                                ForEach(0...9, id: \.self) { number in
                                    Text("\(number)")
                                        .font(font)
                                        .fontWeight(weight)
                                        .frame(width: size.width, height: size.height, alignment: .center)
                                }
                            }
                            .offset(y: -CGFloat(animationRange[index]) * size.height)
                        }
                        .clipped()
                    }
            }
        }
        .onAppear {
            animationRange = Array(repeating: 0, count: "\(value)".count)
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.06) {
                updateText()
            }
        }
        .onChange(of: value) { newValue in
            // 새 값에 따라 animationRange의 길이를 조정합니다.
            adjustAnimationRange(for: newValue)

            // 진행 중인 애니메이션에 대한 목표 값을 업데이트합니다.
            if isAnimating {
                for (index, value) in zip(0..<"\(newValue)".count, "\(newValue)") {
                    animationRange[index] = (String(value) as NSString).integerValue
                }
            } else {
                updateText()
            }
        }
    }
    
    func adjustAnimationRange(for newValue: Int) {
        let newCount = "\(newValue)".count
        let extra = newCount - animationRange.count
        if extra > 0 {
            animationRange += Array(repeating: 0, count: extra)
        } else if extra < 0 {
            animationRange.removeLast(-extra)
        }
    }

    func updateText() {
        isAnimating = true
        let stringValue = "\(value)"
        var longestAnimationDuration = 0.0

        for (index, value) in zip(0..<stringValue.count, stringValue) {
            let fraction = min(Double(index) * 0.15, 0.5)
            let animationDuration = 0.8 + fraction  // 이 값은 애니메이션의 실제 시간에 따라 조정해야 합니다.

            withAnimation(.interactiveSpring(response: 0.8, dampingFraction: 1 + fraction, blendDuration: 1 + fraction)) {
                animationRange[index] = (String(value) as NSString).integerValue
            }

            if animationDuration > longestAnimationDuration {
                longestAnimationDuration = animationDuration
            }
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + longestAnimationDuration) {
            isAnimating = false
        }
    }

}

struct RollingText_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

The issue seems to arise when there’s fast, repeated user interaction. The animation gets stuck for a short period and then continues. I’m looking for a way to make the animation smooth and continuous, even with rapid user inputs.

How can I modify my SwiftUI code to handle rapid changes in the counter value without interrupting the rolling animation effect?

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img