我正在 SwiftUI 中开发一个计数器应用程序,我在其中实现了数字的滚动文本动画。当数字一次增加一个时,动画效果很好。但是,当用户通过快速连续的多个输入快速增加数字时,动画会在恢复之前暂时冻结。
这是我的 RollingText 视图的代码:
//
// 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()
}
}
当用户交互快速、重复时,这个问题似乎就会出现。动画会卡住一小段时间然后继续。我正在寻找一种方法,即使在用户快速输入的情况下,也可以使动画流畅且连续。
如何修改我的 SwiftUI 代码来处理计数器值的快速变化而不中断滚动动画效果?
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 // Adjust this value based on your animation's actual time.
withAnimation(.interactiveSpring(response: 0.8, dampingFraction: 1 + fraction, blendDuration: 1 + fraction)) {
animationRange[index] = (String(value) as NSString).integerValue
}
if animationDuration > longestAnimationDuration {
longestAnimationDuration = animationDuration
}
}
// Cancel ongoing animations before setting new values
withAnimation(nil) {
DispatchQueue.main.asyncAfter(deadline: .now() + longestAnimationDuration) {
isAnimating = false
}
}
}
添加
withAnimation(nil)
会取消所有正在进行的动画,使您的后续动画能够无缝开始。当计数器数字快速变化时,此修改应该会增强滚动动画的行为。