给子类化UIButton添加CABasicAnimation:仅在启用状态生效
嘿,我来帮你解决这个UIButton子类的动画问题!
首先得搞明白为啥重写isEnabled时直接调用colorButton()没效果——核心原因是AutoLayout的布局时机:当你设置isEnabled的时候,AutoLayout可能还在计算按钮的布局(比如frame还没确定),这时候直接触发动画,要么被后续的布局操作覆盖,要么因为视图还没准备好而不生效。而把动画放在layoutSubviews里能工作,是因为这个方法会在AutoLayout完成所有布局计算后才调用,此时按钮的状态已经稳定了。
下面给你两种靠谱的解决方案,按需选就行:
方案一:用标记位配合layoutSubviews(推荐)
这种方法既能保证动画在布局完成后执行,又能避免每次布局都触发动画(只在isEnabled从false变true时触发一次):
class CustomButton: UIButton { // 标记是否需要执行颜色动画 private var shouldAnimateColor = false override var isEnabled: Bool { didSet { // 只有从禁用变启用时,标记需要动画 if isEnabled && !oldValue { shouldAnimateColor = true // 触发布局更新,让layoutSubviews被调用 setNeedsLayout() } else { shouldAnimateColor = false // 禁用时直接恢复默认样式(可根据需求加动画) backgroundColor = .lightGray } } } override func layoutSubviews() { super.layoutSubviews() // 检查标记,执行动画后重置标记 if shouldAnimateColor { colorButton() shouldAnimateColor = false } } func colorButton() { // 确保动画代码正确使用UIView.animate UIView.animate(withDuration: 0.3) { self.backgroundColor = .systemBlue } } }
方案二:异步延迟执行动画
如果你不想修改layoutSubviews,可以在isEnabled的didSet里把动画异步派发到主队列,等当前的布局周期完成后再执行:
class CustomButton: UIButton { override var isEnabled: Bool { didSet { if isEnabled && !oldValue { // 异步到主队列,让AutoLayout先完成布局 DispatchQueue.main.async { self.colorButton() } } else { backgroundColor = .lightGray } } } func colorButton() { UIView.animate(withDuration: 0.3) { self.backgroundColor = .systemBlue } } }
额外检查点
- 确认
colorButton()里的动画代码是正确的——比如你要动画的是backgroundColor还是tintColor,别写错属性; - 视图控制器里手动修改
isEnabled的操作一定要在主队列执行(UI操作必须在主队列); - 如果按钮的约束是动态变化的,避免在动画执行过程中触发布局更新(可以用
UIView.performWithoutAnimation包裹布局相关操作)。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

