SwiftUI ViewModifier动画异常:首次显示与最后消失无过渡
问题:图标显示/消失的动画异常
我写了一个ViewModifier,用来在内容右侧添加图标,打算通过把.clipShape()修饰器的偏移量从-50动画到0来实现图标显示效果。但现在的问题是,图标第一次显示时直接弹出,没有动画;最后一次消失的时候也是同样的情况。
原代码实现
ViewModifier 代码
extension View { func addRightIcon(icon: Image, show: Bool) -> some View { return modifier(RightIconModifier(icon: icon, show: show)) } } struct RightIconModifier: ViewModifier { var icon: Image private var iconMask: Int = 0 init(icon: Image, show: Bool) { self.icon = icon withAnimation(Animation.interpolatingSpring(stiffness: 170, damping: 15).delay(2.5)) { iconMask = show ? 0 : -50 } } func body(content: Content) -> some View { ZStack { content .overlay(rightIcon) } } var rightIcon: some View { icon .font(.system(size: 25)) .foregroundColor(.black) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .trailing) .padding() .clipShape(Rectangle().offset(x: CGFloat(iconMask))) } }
使用示例
TextField(placeholder, text: $text).addRightIcon(icon: Image(systemName: "checkmark"), show: isTextValid) var isTextValid: Bool { if !text.isEmpty { let validation = NSPredicate(format: "SELF MATCHES %@", "[’a-zA-Z]{3,20}") let validated = validation.evaluate(with: text) return validated } return false }
问题原因
原代码的动画逻辑写在init方法里,而init只会在ViewModifier实例创建时执行一次。当视图首次加载时,如果show状态为true,iconMask直接被设置为0,没有触发动画;当show最后一次变为false时,可能因为视图即将被销毁,init不会再次执行,导致动画失效。另外,iconMask是普通属性,SwiftUI无法跟踪其变化,状态更新时无法触发视图重绘和动画。
修复方案
修改代码,让SwiftUI能跟踪状态变化,并在状态切换时正确触发动画:
extension View { func addRightIcon(icon: Image, show: Bool) -> some View { modifier(RightIconModifier(icon: icon, show: show)) } } struct RightIconModifier: ViewModifier { let icon: Image let show: Bool @State private var iconMask: CGFloat = -50 func body(content: Content) -> some View { ZStack { content .overlay(rightIcon) } // 监听show状态变化,触发动画 .onChange(of: show) { newValue in withAnimation(Animation.interpolatingSpring(stiffness: 170, damping: 15).delay(0.25)) { iconMask = newValue ? 0 : -50 } } // 初始状态为true时,触发首次显示动画 .onAppear { if show { withAnimation(Animation.interpolatingSpring(stiffness: 170, damping: 15).delay(0.25)) { iconMask = 0 } } } } var rightIcon: some View { icon .font(.system(size: 25)) .foregroundColor(.black) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .trailing) .padding() .clipShape(Rectangle().offset(x: iconMask)) } }
修改说明
- 将
iconMask改为@State属性,让SwiftUI能够监测其变化,从而触发视图重绘和动画 - 移除
init中的动画逻辑,改用onChange(of: show)监听显示状态的切换,每次状态变化时执行动画 - 添加
onAppear方法处理初始状态为true的场景,确保图标首次显示时有动画效果 - 调整延迟时间为0.25秒(原2.5秒过长,可根据需求自行修改)
内容的提问来源于stack exchange,提问作者Javier Heisecke
相关产品推荐
相关产品推荐

