You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI触发repeat动画时被切换的布尔值最终始终为true如何解决

问题原因

你对SwiftUI的repeatCount动画的作用逻辑存在误解:repeatCount只会重复动画过渡效果,不会重复执行withAnimation闭包内的代码。你在闭包里只调用了一次isAnimating.toggle(),所以无论设置多少次重复,isAnimating的取值只会从初始的false变更为true一次,重复的只是「蓝色到红色」的过渡动画,动画结束后状态自然停留在true,这就是最后背景始终为红色的核心原因。
另外你现有代码中shouldAnimate标记位赋值为true后没有复位,后续再次触发动画时会无法进入判断分支,也属于需要修复的问题。

现有实现方案的缺陷

你用DispatchQueue.main.asyncAfter循环调度的方案虽然能达到效果,但存在两个明显问题:

  • 动画时长和调度间隔强绑定,后续修改动画参数需要同步调整调度逻辑,维护成本高
  • 动画过程中如果页面被销毁,未执行的调度任务仍然会触发,可能出现野访问异常
更合理的实现方案

方案1:使用autoreverses配合延迟复位(最简洁)

利用动画自带的往返效果,仅需要两次状态变更即可实现需求,代码改动最小:
首先修改动画定义:

struct Constants {
    static let animationDuration = 1.0
    static let pulseRepeatTimes = 3 // 脉冲次数,对应6次颜色切换
    static let totalAnimationDuration = animationDuration * 2 * Double(pulseRepeatTimes)
    static let backgroundAlpha: CGFloat = 0.6
}
// 自动往返的重复动画
private let animation = Animation.easeInOut(duration: Constants.animationDuration).repeatCount(Constants.pulseRepeatTimes, autoreverses: true)

然后修改动画触发逻辑:

.onReceive(viewModel.$shouldAnimate, perform: { _ in
    guard viewModel.shouldAnimate else { return }
    // 先复位标记位,方便下次触发
    viewModel.shouldAnimate = false
    // 触发动画
    withAnimation(animation) {
        isAnimating = true
    }
    // 动画结束后自动复位状态
    DispatchQueue.main.asyncAfter(deadline: .now() + Constants.totalAnimationDuration) {
        isAnimating = false
    }
})

该方案不需要循环调度,逻辑清晰,维护成本极低,适合简单的脉冲动画场景。

方案2:使用Timer序列控制状态切换(可控性最高)

如果需要对每一次颜色切换做自定义控制,可以用Combine的Timer Publisher实现,不需要手动管理多个调度任务:

.onReceive(viewModel.$shouldAnimate, perform: { _ in
    guard viewModel.shouldAnimate else { return }
    viewModel.shouldAnimate = false
    // 生成6次定时触发事件,间隔为单段动画时长
    Timer.publish(every: Constants.animationDuration, on: .main, in: .common)
        .autoconnect()
        .prefix(6) // 触发6次后自动结束
        .sink { [weak self] _ in
            withAnimation(.easeInOut(duration: Constants.animationDuration)) {
                self?.isAnimating.toggle()
            }
        }
        .store(in: &viewModel.cancellables)
})

该方案会自动在触发指定次数后取消订阅,不存在内存泄漏风险,灵活度更高,适合需要自定义每段动画逻辑的场景。

内容的提问来源于stack exchange,提问作者DevB1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 11:45:04