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?
I understand what you're aiming for, and it seems like the issue might be arising due to the branching logic in that particular section. To reflect changing numbers smoothly, instead of abruptly stopping the current animation, it would be visually more natural to let the existing animation complete fully before starting a new one. Try removing the if statement in that section and give it a go.
If the problem you're describing isn't this, then it's likely a throttling issue due to continuous function calls. In that case, you might need to increase the delay further or explore more robust ways of managing animation states. I hope my suggestion proves helpful.