SwiftUI动画与scaleEffect异常行为排查及修复咨询
问题原因
每次切换scale值时,SwiftUI会为新的scale变化创建一个新的无限重复动画,但之前的无限动画并未被终止,多个动画叠加后导致UI行为紊乱,出现看似多个脉动效果叠加的异常。
修复方案
核心思路是确保每次切换目标缩放比例时,终止旧的无限动画并启动新的连贯动画,避免叠加。以下是两种可行的实现方式:
方式一:显式控制动画生命周期
通过onAppear和onChange监听目标缩放比例的变化,每次变化时重置动画状态并启动新的无限动画:
import SwiftUI struct AnimatedView: View { let targetScale: CGFloat @State private var currentScale: CGFloat = 1.0 var body: some View { Circle() .frame(width: 100, height: 100) .scaleEffect(currentScale) .onAppear { startPulsing() } .onChange(of: targetScale) { _ in startPulsing() } } private func startPulsing() { // 先重置缩放比例到初始值,避免动画叠加 currentScale = 1.0 // 启动新的无限脉动动画 withAnimation(Animation.easeInOut.repeatForever(autoreverses: true)) { currentScale = targetScale } } } struct TestAnimation: View { @State private var large_pulsate: Bool = false var body: some View { AnimatedView(targetScale: large_pulsate ? 2.0 : 1.1) .onTapGesture { large_pulsate.toggle() } } } struct TestAnimation_Previews: PreviewProvider { static var previews: some View { TestAnimation() } }
方式二:使用动画修饰符直接绑定无限动画
另一种更简洁的方式是将无限动画直接应用到视图上,通过动态修改动画的目标值来切换脉动尺寸,确保动画始终只有一个实例:
import SwiftUI struct AnimatedView: View { let maxScale: CGFloat @State private var isPulsing = false var body: some View { Circle() .frame(width: 100, height: 100) .scaleEffect(isPulsing ? maxScale : 1.0) .animation(Animation.easeInOut.repeatForever(autoreverses: true), value: isPulsing) .onAppear { isPulsing = true } .onChange(of: maxScale) { _ in // 切换目标尺寸时,短暂重置动画状态以避免叠加 isPulsing = false DispatchQueue.main.async { isPulsing = true } } } } struct TestAnimation: View { @State private var large_pulsate: Bool = false var body: some View { AnimatedView(maxScale: large_pulsate ? 2.0 : 1.1) .onTapGesture { large_pulsate.toggle() } } }
两种方式都能保证每次切换时只有一个无限动画在运行,解决动画叠加导致的紊乱问题,实现点击时在大、小尺寸脉动效果间平滑切换的预期行为。
内容的提问来源于stack exchange,提问作者Discombobulous
相关产品推荐
相关产品推荐

