Swift中如何实现UIButton的依次脉动动画序列?
没问题,要让按钮按顺序逐个执行脉动动画,核心是得等前一个按钮的动画完全结束后,再触发下一个的动画。这里给你两种实用的方案:
方案一:用动画完成回调(推荐)
首先我们需要给你的pulsate()方法加一个完成回调,这样能精准捕获动画结束的时机。修改你的UIButton扩展如下:
extension UIButton { func pulsate(completion: (() -> Void)? = nil) { let pulse = CASpringAnimation(keyPath: "transform.scale") pulse.duration = 0.3 pulse.fromValue = 0.95 pulse.toValue = 1.05 pulse.autoreverses = true pulse.repeatCount = 3 pulse.initialVelocity = 0.5 pulse.damping = 1.0 // 存储完成回调,用关联对象避免循环引用 objc_setAssociatedObject(self, &AssociatedKeys.pulseCompletion, completion, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) pulse.delegate = self layer.add(pulse, forKey: "pulse") } private enum AssociatedKeys { static var pulseCompletion = "pulseCompletion" } } // 实现CAAnimationDelegate来监听动画结束 extension UIButton: CAAnimationDelegate { public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { guard let completion = objc_getAssociatedObject(self, &AssociatedKeys.pulseCompletion) as? (() -> Void) else { return } if flag { completion() } // 清理关联对象,防止内存泄漏 objc_setAssociatedObject(self, &AssociatedKeys.pulseCompletion, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } }
然后写一个递归方法来逐个触发按钮动画:
private func animateButtonsSequentially(startingAt index: Int = 0) { guard index < buttonsOutlet.count else { return } // 所有按钮动画完成,退出 let currentButton = buttonsOutlet[index] currentButton.pulsate { [weak self] in // 当前按钮动画结束后,递归调用下一个 self?.animateButtonsSequentially(startingAt: index + 1) } } // 闲置检测时调用这个方法 private func onInactivityDetected() { animateButtonsSequentially() }
这种方法的优势是完全基于动画的实际结束时机,哪怕后续你修改了动画的时长、重复次数等参数,也不需要调整其他代码,非常可靠。
方案二:用DispatchQueue延迟触发(简洁版)
如果你不想修改动画扩展的结构,也可以通过手动计算每个按钮的延迟时间来实现顺序动画:
private func onInactivityDetected() { // 计算单个按钮动画的总时长:duration * 2(自动反转)* repeatCount let singleAnimationTotalTime = 0.3 * 2 * 3 for (index, button) in buttonsOutlet.enumerated() { let delay = Double(index) * singleAnimationTotalTime DispatchQueue.main.asyncAfter(deadline: .now() + delay) { button.pulsate() } } }
这种方法代码更短,但缺点是依赖手动计算总时长,如果后续修改了动画的参数(比如把repeatCount改成2),必须同步更新这里的计算,不然会出现动画重叠或者间隔不对的问题。
内容的提问来源于stack exchange,提问作者John Smith Optional
相关产品推荐
相关产品推荐

