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

Swift中两个触发长动画的函数如何实现互相停止对方动画

两个触发长动画的函数互斥终止方案

核心逻辑很简单,不需要引入复杂的状态管理,用UIKit自带的动画终止接口,配合一个简单的运行状态标记就能实现。

实现步骤

  • 首先定义枚举标记当前运行的动画类型,比用多个布尔值判断更不容易出状态冲突
  • 每个函数触发时,第一步先终止所有正在运行的动画,把视图状态重置到初始位置,避免动画卡在中间帧
  • 启动当前动画前更新状态标记,动画自然结束后清空标记即可

具体代码实现

首先给你的ViewController添加对应属性:

// 标记当前正在执行的动画类型
private enum RunningAnimation {
    case first, second, none
}
// 持有当前运行动画的状态,默认无运行中的动画
private var currentRunningAnim: RunningAnimation = .none
// 替换成你实际执行动画的视图引用,多视图做动画就对应加多个引用
@IBOutlet weak var animationContainerView: UIView!

修改第一个函数的逻辑:

func firstFunc() {
    // 终止所有正在执行的动画
    animationContainerView.layer.removeAllAnimations()
    
    // 如果之前运行的是第二个函数的动画,手动重置视图到初始状态
    if currentRunningAnim == .second {
        animationContainerView.transform = .identity
        animationContainerView.alpha = 1
        // 这里补全你第二个动画里修改过的所有视图属性,全部恢复为初始值
    }
    
    Alert.show()
    // 更新状态标记为第一个动画运行中
    currentRunningAnim = .first
    
    // 启动第一个长动画
    UIView.animate(withDuration: 4, animations: {
        // 写你第一个动画的实际效果,比如位移、变色等
        self.animationContainerView.center.x += 120
    }, completion: { [weak self] isFinished in
        guard let self = self else { return }
        // 只有动画自然结束、没有被手动打断的时候才清空状态
        if isFinished && self.currentRunningAnim == .first {
            self.currentRunningAnim = .none
        }
    })
}

修改第二个函数的逻辑:

func secondFunc() {
    // 终止所有正在执行的动画
    animationContainerView.layer.removeAllAnimations()
    
    // 如果之前运行的是第一个函数的动画,手动重置视图到初始状态
    if currentRunningAnim == .first {
        animationContainerView.transform = .identity
        animationContainerView.alpha = 1
        // 这里补全你第一个动画里修改过的所有视图属性,全部恢复为初始值
    }
    
    Alert.show()
    // 更新状态标记为第二个动画运行中
    currentRunningAnim = .second
    
    // 启动第二个长动画
    UIView.animate(withDuration: 4, animations: {
        // 写你第二个动画的实际效果,比如缩放、旋转等
        self.animationContainerView.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
    }, completion: { [weak self] isFinished in
        guard let self = self else { return }
        // 只有动画自然结束、没有被手动打断的时候才清空状态
        if isFinished && self.currentRunningAnim == .second {
            self.currentRunningAnim = .none
        }
    })
}

注意事项

  • 必须用block形式的UIView动画接口,老版beginAnimations/commitAnimations接口无法通过removeAllAnimations()可靠终止
  • 调用removeAllAnimations()后动画会直接停在当前中间帧,必须手动把所有动画修改的属性重置为初始值,否则下一个动画启动时会出现跳帧
  • 完成回调里要判断isFinished和当前状态标记再清空,避免被打断的旧动画回调误改新动画的运行状态
  • 如果两个动画作用在不同视图上,只需要在函数开头对所有参与动画的视图都调用一次layer.removeAllAnimations(),再对应重置状态即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 04:15:29