SwiftUI中更新值后.repeatForever()动画自动反转失效问题
SwiftUI闲置动画问题:自动反转功能失效
我想实现一个闲置触发的按钮动画:屏幕3秒无触摸时,按钮沿Y轴偏移15pt并循环往返(自动反转);触摸屏幕时,按钮立即回到初始位置。但当前实现存在问题:按钮回到原位后,再次进入闲置状态时,动画的autoreverses功能不再生效,按钮只会停在偏移15pt的位置,无法往返运动。
原实现代码
GoLiveButton组件代码
struct GoLiveButton: View { @State private var animationOffset: CGFloat = 0 @Binding var isIdle: Bool var body: some View { ZStack { Button(action: {} ) { Text("Go Live") .frame(width: 120, height: 40) .background(Color.black) .foregroundColor(.white) .clipShape(Capsule()) .font(.system(size: 20)) .shadow(color: .black, radius: 4, x: 4, y: 4) } .offset(y: animationOffset) .animation(.timingCurve(0.38, 0.07, 0.12, 0.93, duration: 2).repeatForever(autoreverses: true), value: isIdle) .animation(.timingCurve(0.38, 0.07, 0.12, 0.93, duration: 2), value: !isIdle) } .onAppear { self.isIdle = true self.animationOffset = 15 } .onChange(of: isIdle) { newValue in if newValue { self.animationOffset = 15 } else { self.animationOffset = 0 } } } }
闲置检测视图代码
struct StackOverflowView: View { @State private var timer: Timer? @State private var isIdle = false var body: some View { GeometryReader { geo in GoLiveButton(isIdle: $isIdle) } .onTapGesture { print("DEBUG: CustomTabView OnTapGesture Triggered") self.isIdle = false self.timer?.invalidate() self.timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in self.isIdle = true } } .gesture( DragGesture().onEnded { _ in self.isIdle = false self.timer?.invalidate() self.timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in self.isIdle = true } } ) } }
修复方案
问题核心在于:原代码通过绑定isIdle状态直接触发重复动画,状态切换时SwiftUI的动画系统无法正确重置循环状态;同时多动画绑定导致逻辑冲突。以下是修复后的代码:
修改后的GoLiveButton组件
struct GoLiveButton: View { @State private var animationOffset: CGFloat = 0 @Binding var isIdle: Bool // 抽离闲置动画常量,统一配置 private let idleAnimation = Animation.timingCurve(0.38, 0.07, 0.12, 0.93, duration: 2) .repeatForever(autoreverses: true) var body: some View { Button(action: {}) { Text("Go Live") .frame(width: 120, height: 40) .background(Color.black) .foregroundColor(.white) .clipShape(Capsule()) .font(.system(size: 20)) .shadow(color: .black, radius: 4, x: 4, y: 4) } .offset(y: animationOffset) .onAppear { startIdleAnimation() } .onChange(of: isIdle) { newValue in if newValue { startIdleAnimation() } else { // 快速回到初始位置,可自定义动画速度 withAnimation(idleAnimation.speed(2)) { animationOffset = 0 } } } } // 手动触发循环动画,确保动画系统正确初始化 private func startIdleAnimation() { animationOffset = 0 DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { withAnimation(idleAnimation) { animationOffset = 15 } } } }
简化后的闲置检测视图
struct StackOverflowView: View { @State private var timer: Timer? @State private var isIdle = false var body: some View { GeometryReader { _ in GoLiveButton(isIdle: $isIdle) } .onTapGesture(perform: resetIdleTimer) .gesture( DragGesture().onEnded { _ in resetIdleTimer() } ) } // 抽离重置计时器逻辑,避免代码重复 private func resetIdleTimer() { print("DEBUG: Interaction detected") isIdle = false timer?.invalidate() timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in isIdle = true } } }
修复说明
- 将循环动画抽为常量,避免重复定义导致的配置不一致
- 通过
startIdleAnimation方法手动触发动画:先重置偏移量,再延迟启动循环动画,确保SwiftUI动画系统能正确初始化循环状态 - 退出闲置时使用
withAnimation控制返回动画,可自定义速度提升交互体验 - 简化闲置检测视图的逻辑,抽离重复代码,提升可读性
内容的提问来源于stack exchange,提问作者grandsirr
相关产品推荐
相关产品推荐

