SwiftUI中如何让加载动画切换refreshing时完成当前周期
解决SwiftUI加载动画切换时无法完成当前周期的问题
当前实现的问题在于,直接通过refreshing绑定值切换视图分支,会导致动画被强制中断,无法完成当前旋转周期。要实现动画优雅完成当前周期后再停止,需要调整状态管理和动画控制逻辑:
修改后的完整实现
import SwiftUI public struct RefreshingView : View { @State private var rotation = Angle(degrees: 0.0) @Binding private var refreshing: Bool // 新增状态变量,控制动画实际播放状态 @State private var isAnimating = false public init(refreshing: Binding<Bool>) { _refreshing = refreshing } public var body: some View { Image(systemName: "arrow.2.circlepath") .foregroundColor(.red) .rotationEffect(rotation) .onChange(of: refreshing) { newValue in if newValue { // 开始动画 isAnimating = true startRotationAnimation() } else { // 标记停止,等待当前周期结束后复位 isAnimating = false } } .onAppear { if refreshing { isAnimating = true startRotationAnimation() } } } private func startRotationAnimation() { guard isAnimating else { return } // 执行单次180度旋转动画,时长1秒 withAnimation(.linear(duration: 1)) { rotation = rotation + Angle(degrees: -180) } completion: { // 动画完成后判断是否继续循环 if isAnimating { startRotationAnimation() } else { // 停止后复位到初始角度 withAnimation(.linear(duration: 0.3)) { rotation = .zero } } } } }
关键改进说明
- 分离状态控制:用
isAnimating状态变量单独控制动画循环,避免直接依赖refreshing绑定值导致视图切换中断动画 - 手动循环动画:放弃
repeatForever,改用单次动画+完成回调的方式手动控制循环,这样可以在refreshing变为false时,让当前动画完成后再停止 - 平滑复位:动画停止后,通过一个短动画将旋转角度复位到初始状态,避免突兀跳转
- 统一视图结构:移除原有的
if-else视图分支,始终显示同一个Image,消除视图切换带来的视觉断层
低版本兼容方案
如果需要兼容iOS 15以下版本(withAnimation的completion回调仅iOS15+支持),可以改用延迟回调判断周期结束:
private func startRotationAnimation() { guard isAnimating else { return } withAnimation(.linear(duration: 1)) { rotation = rotation + Angle(degrees: -180) } DispatchQueue.main.asyncAfter(deadline: .now() + 1) { if isAnimating { startRotationAnimation() } else { withAnimation(.linear(duration: 0.3)) { rotation = .zero } } } }
内容的提问来源于stack exchange,提问作者Niklas
相关产品推荐
相关产品推荐

