如何停止LazyVGrid全局动画?滚动时未显示元素仍抖动
问题分析
问题出在LazyVGrid的懒加载特性和手动管理动画状态的冲突上:
- 关闭抖动开关(
isShaking = false)时,可视区内的元素会通过onChange回调重置旋转角度,但未加载的元素滚动进入可视区时,其@State变量rotationAngle可能保留了复用视图的旧动画状态,或者手动启动的repeatForever动画未被正确终止,导致即使isShaking为false仍持续抖动。 - 手动控制
rotationAngle和动画的方式,无法让SwiftUI的动画系统正确跟踪状态变化,尤其在懒加载视图复用场景下容易出现状态残留。
解决方案
重构GridElementView的动画逻辑,去掉手动维护的rotationAngle状态,直接通过isShaking值控制旋转角度和动画,让SwiftUI自动处理动画的启停与视图复用的状态重置:
struct GridElementView: View { let index: String let isShaking: Bool let onDelete: () -> Void var body: some View { Button { onDelete() } label: { ZStack { RoundedRectangle(cornerRadius: 10) .fill(Color.blue) .frame(height: 250) Text("\(index)") .font(.headline) .foregroundColor(.white) } // 根据isShaking状态控制旋转角度 .rotationEffect(.degrees(isShaking ? 2.5 : 0)) // 绑定动画到isShaking,自动处理启停逻辑 .animation( isShaking ? .linear(duration: 0.15).repeatForever(autoreverses: true) : .interactiveSpring(), value: isShaking ) } } }
同时移除GridElementView中未使用的ViewModel扩展,减少冗余代码。
方案生效原因
- 状态驱动动画:直接通过
isShaking值控制旋转角度,无需手动维护rotationAngle状态,彻底避免了复用视图时的状态残留问题。 - 自动动画管理:
animation修饰符绑定到isShaking值,当状态变化时,SwiftUI会自动启动/终止对应动画——包括懒加载视图刚进入可视区时,会直接根据当前isShaking状态应用正确的样式和动画状态。 - 适配懒加载特性:LazyVGrid复用视图时,新元素会继承当前
isShaking状态,不会保留旧视图的动画残留。
额外优化
如果需要更平滑的停止过渡,可以将关闭时的动画类型替换为.easeOut(duration: 0.2),根据需求调整过渡效果。
内容的提问来源于stack exchange,提问作者Sergei Kirasirov
相关产品推荐
相关产品推荐

