iOS17中PhaseAnimator循环动画视图销毁后崩溃问题求助
iOS 17 PhaseAnimator 返回视图后崩溃问题排查与解决
使用iOS 17新增的PhaseAnimator实现循环呼吸动画时,出现以下问题:推送PhaseView视图时动画正常,但从该视图返回后,应用无响应甚至崩溃。排查发现:
- 不对Circle视图做属性更新时无异常
- 使用
phaseAnimator尾随函数也存在相同问题 - 结合trigger与定时器模拟循环动画则不会崩溃
崩溃场景代码
enum OuterBreathState: CaseIterable { case exhale, inhale var scale: CGFloat { switch self { case .inhale: return 2 case .exhale: return 1 } } var color: Color { switch self { case .inhale: return .white case .exhale: return .blue } } } struct PhaseView: View { var body: some View { PhaseAnimator(OuterBreathState.allCases) { state in Circle() .background(.clear) .frame(width: 150, height: 150) .padding(.vertical) .scaleEffect(state.scale) .background(in: Circle()) .foregroundStyle(state.color) } animation: {state in switch state { case .inhale: return .easeInOut(duration: 1.5) case .exhale: return .easeInOut(duration: 3.5) } } } } struct ContentView: View { var body: some View { NavigationStack { NavigationLink("push") { PhaseView() } } } }
正常场景代码
enum OuterBreathState: CaseIterable { case exhale, inhale var scale: CGFloat { switch self { case .inhale: return 2 case .exhale: return 1 } } var color: Color { switch self { case .inhale: return .white case .exhale: return .blue } } } struct PhaseView: View { @State var animate = false let timer = Timer.publish(every: 5, tolerance: .zero, on: .main, in: .default).autoconnect() var body: some View { PhaseAnimator(OuterBreathState.allCases, trigger: animate) { state in Circle() .background(.clear) .frame(width: 150, height: 150) .padding(.vertical) .scaleEffect(state.scale) .background(in: Circle()) .foregroundStyle(state.color) } animation: {state in switch state { case .inhale: return .easeInOut(duration: 1.5) case .exhale: return .easeInOut(duration: 3.5) } } .onReceive(timer, perform: { _ in animate.toggle() }) .onTapGesture { animate.toggle() } } } struct ContentView: View { var body: some View { NavigationStack { NavigationLink("push") { PhaseView() } } } } #Preview { ContentView() }
崩溃原因
这是iOS 17.0至17.2版本中PhaseAnimator的已知bug:当使用无trigger的自动循环动画时,视图被销毁(返回上一页)后,PhaseAnimator内部的动画循环或定时器未被正确取消,导致主线程被持续占用,进而引发应用无响应或崩溃。而带trigger的版本中,trigger绑定的@State变量会随视图销毁而被释放,定时器也会自动停止,动画循环因此终止,避免了崩溃。
解决方案
1. 升级iOS版本
将设备或模拟器的iOS版本升级至17.3及以上,苹果在该版本中修复了PhaseAnimator的生命周期管理问题。
2. 手动管理动画生命周期
在视图消失时主动停止动画,通过添加trigger并在onDisappear中更新状态来终止循环:
struct PhaseView: View { @State private var isAnimating = true var body: some View { PhaseAnimator(OuterBreathState.allCases, trigger: isAnimating) { state in Circle() .background(.clear) .frame(width: 150, height: 150) .padding(.vertical) .scaleEffect(state.scale) .background(in: Circle()) .foregroundStyle(state.color) } animation: { state in switch state { case .inhale: return .easeInOut(duration: 1.5) case .exhale: return .easeInOut(duration: 3.5) } } .onDisappear { isAnimating = false } } }
3. 替代实现方案
若无法升级系统,可使用withAnimation结合定时器实现相同的呼吸动画效果:
struct PhaseView: View { @State private var scale: CGFloat = 1 @State private var color: Color = .blue let timer = Timer.publish(every: 5, on: .main, in: .default).autoconnect() var body: some View { Circle() .background(.clear) .frame(width: 150, height: 150) .padding(.vertical) .scaleEffect(scale) .background(in: Circle()) .foregroundStyle(color) .onReceive(timer) { _ in withAnimation(.easeInOut(duration: 1.5)) { scale = 2 color = .white } DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { withAnimation(.easeInOut(duration: 3.5)) { scale = 1 color = .blue } } } .onDisappear { timer.upstream.connect().cancel() } } }
内容的提问来源于stack exchange,提问作者Khal
相关产品推荐
相关产品推荐

