SwiftUI字体抖动动画Bug问题求助
SwiftUI Chips选中时动画失效问题解决
问题现象
ChipsView在选中元素时,字体粗细与颜色的动画无法正常执行,但取消选中时动画表现正常。已尝试更换所有动画类型、切换至主线程等操作,均未解决。

问题根源
SwiftUI默认无法对Font.Weight这类枚举类型的切换进行平滑动画——枚举类型不支持插值计算,系统无法生成中间过渡状态,导致选中时的动画直接跳变。而取消选中时的异常“正常”表现,是系统对反向状态切换的偶然兼容,并非真正解决了问题。
解决方案
将枚举类型的字体粗细转换为可动画的数值类型,通过数值插值实现平滑过渡。以下是两种可行的实现方式:
方法一:使用自定义AnimatableModifier
通过自定义动画修饰符,将Font.Weight的切换映射到可插值的Double数值:
struct AnimatedFontWeightModifier: AnimatableModifier { var weight: Double let selectedWeight: Font.Weight = .bold let unselectedWeight: Font.Weight = .light var animatableData: Double { get { weight } set { weight = newValue } } func body(content: Content) -> some View { content .font(.system(size: 56, weight: weight > 0.5 ? selectedWeight : unselectedWeight)) } } struct ChipsView: View { let title: String @Binding var selectedString: String private var isSelected: Bool { selectedString == title } private let selectedColor = Color.red private let unselectedColor = Color.gray @State private var animationProgress: Double = 0 var body: some View { Text(title) .foregroundColor(isSelected ? selectedColor : unselectedColor) .modifier(AnimatedFontWeightModifier(weight: animationProgress)) .contentShape(Rectangle()) .onTapGesture { withAnimation(.bouncy(duration: 0.3)) { selectedString = isSelected ? "" : title animationProgress = isSelected ? 0 : 1 } } .onAppear { animationProgress = isSelected ? 1 : 0 } .onChange(of: isSelected) { newValue in animationProgress = newValue ? 1 : 0 } } }
方法二:直接绑定数值型变量控制字体粗细
简化实现,用Double变量直接驱动字体粗细的切换:
struct ChipsView: View { let title: String @Binding var selectedString: String private var isSelected: Bool { selectedString == title } private let selectedColor = Color.red private let unselectedColor = Color.gray @State private var fontWeightProgress: Double = 0 var body: some View { Text(title) .font(.system(size: 56)) .fontWeight(fontWeightProgress > 0.5 ? .bold : .light) .foregroundColor(isSelected ? selectedColor : unselectedColor) .contentShape(Rectangle()) .onTapGesture { withAnimation(.bouncy(duration: 0.3)) { selectedString = isSelected ? "" : title fontWeightProgress = isSelected ? 0 : 1 } } .onAppear { fontWeightProgress = isSelected ? 1 : 0 } .onChange(of: isSelected) { fontWeightProgress = $0 ? 1 : 0 } } }
效果说明
两种方法均通过数值插值让SwiftUI生成平滑的过渡状态,同时颜色动画也会随状态切换正常执行,解决选中时动画失效的问题。
内容的提问来源于stack exchange,提问作者zef_s
相关产品推荐
相关产品推荐

