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

自定义脉冲UIButton动画期间无法响应触摸事件问题咨询

解决PulseButton动画期间无法响应触摸的问题

我之前也碰到过类似的坑,核心问题其实有两个:一是默认的UIView.animate会禁用视图的用户交互,二是直接修改按钮的transform会导致视觉大小和实际可点击区域不匹配。下面给你两种可行的解决方案:

方案一:开启动画交互并修正点击区域

首先,我们需要在动画方法里加上.allowUserInteraction选项,让视图在动画期间依然能响应触摸。然后重写hitTest方法,确保点击视觉上的按钮区域都能被识别。

修改你的动画代码:

func grow() {
    UIView.animate(withDuration: 0.8, delay: 0, options: .allowUserInteraction) {
        self.transform = CGAffineTransform(scaleX: 2.5, y: 2.5)
        self.layoutIfNeeded()
    }
}

func shrink() {
    UIView.animate(withDuration: 0.8, delay: 0, options: .allowUserInteraction) {
        self.transform = .identity
        self.layoutIfNeeded()
    }
}

接着给PulseButton添加hitTest的重写:

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    // 计算缩放后的按钮边界
    let scaledBounds = bounds.applying(transform)
    // 将点击点转换到未缩放的坐标系下
    let inverseTransform = CGAffineTransform(scaleX: 1/transform.a, y: 1/transform.d)
    let scaledPoint = point.applying(inverseTransform)
    
    return scaledBounds.contains(scaledPoint) ? self : nil
}

这样修改后,动画期间按钮就能正常响应触摸,而且点击放大后的按钮任何位置都会触发事件。

方案二:用子视图实现动画(更推荐)

直接修改按钮的transform容易带来交互区域的适配问题,更优雅的方式是让按钮本身保持原始状态,用一个子视图来做缩放动画。这样按钮的交互区域始终不变,完全不会影响触摸响应。

重构后的PulseButton代码:

class PulseButton: UIButton {
    // 专门用来做脉冲动画的子视图
    private let pulseView = UIView()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setup()
    }
    
    private func setup() {
        // 配置脉冲视图,和按钮外观保持一致
        pulseView.frame = bounds
        pulseView.backgroundColor = self.backgroundColor
        pulseView.layer.cornerRadius = self.layer.cornerRadius
        pulseView.isUserInteractionEnabled = false // 禁止子视图抢交互,让按钮本身处理
        // 把脉冲视图放在按钮最底层,不影响按钮的文字/图标
        insertSubview(pulseView, at: 0)
        
        // 启动定时动画
        Timer.scheduledTimer(timeInterval: 1.6, target: self, selector: #selector(animatePulse), userInfo: nil, repeats: true)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        // 按钮布局变化时,同步更新脉冲视图的大小和圆角
        pulseView.frame = bounds
        pulseView.layer.cornerRadius = self.layer.cornerRadius
    }
    
    @objc private func animatePulse() {
        // 重置脉冲视图的状态
        pulseView.transform = .identity
        pulseView.alpha = 1
        
        // 执行放大+淡出动画,视觉效果更自然
        UIView.animate(withDuration: 0.8) {
            self.pulseView.transform = CGAffineTransform(scaleX: 2.5, y: 2.5)
            self.pulseView.alpha = 0.3
        } completion: { _ in
            // 动画结束后重置透明度,为下一次动画做准备
            self.pulseView.alpha = 1
        }
    }
}

这种方式下,按钮本身的所有交互逻辑都不受影响,动画完全由子视图完成,视觉效果和原来一致,但稳定性更高,也更容易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:31:03