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

Swift UIKit:如何让顺序动画函数循环执行N次?

问题

现有一个upDown()函数,通过UIView.animate实现两个ImageView的上下脉冲动画(分别重复4次和2次),运行正常。但需要让整个upDown()的动画流程按顺序循环执行指定次数(如5次或25次)。尝试过用其他UIView.animate包裹、for-in循环均未成功,项目禁止使用Pods,需要找到可行的原生实现方案。

现有代码:

func upDown() {
    runButton.isEnabled = false

    UIView.animate(
        withDuration: 0.5,
        delay: 0,
        options: [.autoreverse, .repeat]) {
            UIView.modifyAnimations(withRepeatCount: 4, autoreverses: true) {
                self.arrowDownImageView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
                self.arrowDownImageView.tintColor = .green
                self.downLabel.backgroundColor = .green
            }
        } completion: { _ in
            self.arrowDownImageView.transform = .identity
            self.downLabel.transform  = .identity
            self.downLabel.backgroundColor = .black
            self.arrowDownImageView.tintColor = .black
            
            UIView.animate(
                withDuration: 0.5,
                delay: 0,
                options: [.autoreverse, .repeat]) {
                    UIView.modifyAnimations(withRepeatCount: 2, autoreverses: true) {
                        self.arrowUpImageView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)

                        self.arrowUpImageView.tintColor = .blue
                        self.upLabel.backgroundColor = .blue
                    }
                } completion: { _ in
                    self.arrowUpImageView.transform = .identity
                    self.upLabel.transform = .identity
                    self.upLabel.backgroundColor = .black
                    self.arrowUpImageView.tintColor = .black
                    
                    self.runButton.isEnabled = true
                }
        }
}
解决方案

核心思路是把单次完整的上下动画流程(down动画→up动画)封装成独立方法,通过计数器+递归调用适配动画的异步特性,实现指定次数的循环。

步骤1:封装单次完整动画流程

将原来upDown()里的逻辑抽成带完成回调的方法,明确单次动画流程的结束时机:

func runSingleUpDownCycle(completion: @escaping () -> Void) {
    // 执行down动画
    UIView.animate(
        withDuration: 0.5,
        delay: 0,
        options: [.autoreverse, .repeat]) {
            UIView.modifyAnimations(withRepeatCount: 4, autoreverses: true) {
                self.arrowDownImageView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
                self.arrowDownImageView.tintColor = .green
                self.downLabel.backgroundColor = .green
            }
        } completion: { _ in
            // 重置down控件状态
            self.arrowDownImageView.transform = .identity
            self.downLabel.transform = .identity
            self.downLabel.backgroundColor = .black
            self.arrowDownImageView.tintColor = .black
            
            // 执行up动画
            UIView.animate(
                withDuration: 0.5,
                delay: 0,
                options: [.autoreverse, .repeat]) {
                    UIView.modifyAnimations(withRepeatCount: 2, autoreverses: true) {
                        self.arrowUpImageView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
                        self.arrowUpImageView.tintColor = .blue
                        self.upLabel.backgroundColor = .blue
                    }
                } completion: { _ in
                    // 重置up控件状态
                    self.arrowUpImageView.transform = .identity
                    self.upLabel.transform = .identity
                    self.upLabel.backgroundColor = .black
                    self.arrowUpImageView.tintColor = .black
                    
                    // 单次流程完成,触发回调
                    completion()
                }
        }
}

步骤2:实现带循环次数的控制方法

新增方法接收循环次数参数,通过递归调用单次动画方法,直到完成指定次数:

func runUpDownLoop(totalCycles: Int) {
    runButton.isEnabled = false
    var completedCycles = 0
    
    func executeNextCycle() {
        completedCycles += 1
        
        runSingleUpDownCycle { [weak self] in
            guard let self = self else { return }
            
            if completedCycles < totalCycles {
                // 未完成所有次数,继续下一轮
                executeNextCycle()
            } else {
                // 所有循环结束,恢复按钮状态
                self.runButton.isEnabled = true
            }
        }
    }
    
    // 启动第一次循环
    executeNextCycle()
}

步骤3:调用方式

直接调用runUpDownLoop(totalCycles: 5)即可让整个动画流程循环5次,替换原来的upDown()调用即可。

补充说明

  • 使用[weak self]避免循环引用,防止内存泄漏
  • 递归调用天然适配动画的异步特性,确保每一轮动画都在上一轮完全结束后才开始
  • 计数器completedCycles准确跟踪循环进度,完成指定次数后自动停止并恢复交互状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 01:55:17