如何实现UIButton快速连续点击时每次触发背景色淡入淡出效果
自定义键盘按钮快速点击淡入淡出失效问题修复
问题场景
自定义数字键盘按钮继承自UIButton,通过重写isHighlighted属性监听按压状态,切换背景色实现「按下显示浅灰、松开0.2秒淡回默认灰色」的交互效果,初始实现代码如下:
class NumpadButton: UIButton { override var isHighlighted: Bool { didSet { if isHighlighted { backgroundColor = UIColor(named: "FadeGrayColor") } else { UIView.animate(withDuration: 0.2) { self.backgroundColor = UIColor(named: "NumpadGrayColor") } } } } }
存在问题
- 极快连续点击同一个按钮时,第一次点击后的后续点击不会触发背景色淡入淡出变化
- 问题原因:时长0.2秒的淡出动画未执行完成时,新点击触发的状态更新会被正在运行的动画阻塞,无法立刻响应
- 参考系统自带计算器的交互:无论点击速度多快,每次点击都能正常触发淡入淡出效果
- 其他尝试的问题:直接移除动画代码会导致背景色无过渡硬切换,不符合预期;换用
touchesBegan()/touchesEnded()监听按压状态,存在完全相同的问题
修复方案
问题核心原因是UIView动画默认不会被新的属性赋值打断,上一段未完成的动画会阻塞新的状态更新。只需要在每次触发状态切换前,先终止当前正在运行的动画、同步当前屏幕上实际显示的颜色值到控件属性,再执行新的逻辑即可复现系统计算器的交互效果。
修正后的完整代码:
class NumpadButton: UIButton { override var isHighlighted: Bool { didSet { // 同步当前渲染层显示的实际颜色,终止正在运行的动画 if let currentShowColor = layer.presentation()?.backgroundColor { layer.backgroundColor = currentShowColor } layer.removeAllAnimations() if isHighlighted { // 按下时直接切换高亮色,无需过渡动画 backgroundColor = UIColor(named: "FadeGrayColor") } else { // 松开时执行淡入动画回到默认底色 UIView.animate(withDuration: 0.2, delay: 0, options: [.allowUserInteraction, .beginFromCurrentState]) { self.backgroundColor = UIColor(named: "NumpadGrayColor") } } } } }
关键逻辑说明
layer.presentation()可以拿到当前屏幕上实际渲染的图层状态,读取它的背景色赋值给当前layer,是为了避免动画终止后控件跳回动画起点/终点的颜色,保证视觉连续性layer.removeAllAnimations()会直接清空当前控件上所有未完成的动画,让后续的属性赋值立刻生效- 动画选项加上
.allowUserInteraction和.beginFromCurrentState,是为了保证动画执行过程中依然可以响应点击交互,且新动画会从当前显示的颜色状态开始执行,不会出现跳色
内容的提问来源于stack exchange,提问作者artexhibit
相关产品推荐
相关产品推荐

