子类化 UIButton 并覆盖触摸事件 - 不起作用

Subclassing UIButton and overriding touch events - not working

我需要为项目中的所有按钮制作比例 spring 动画。 所以我将 UIButton 子类化并覆盖触摸事件函数。

import UIKit

class UIAnimatedButton: UIButton {

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {

    UIView.animateWithDuration(0.1, animations: { () -> Void in
        self.transform = CGAffineTransformMakeScale(0.8, 0.8)

    })
    super.touchesBegan(touches, withEvent: event)

}

override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {


    UIView.animateWithDuration(0.5,
        delay: 0,
        usingSpringWithDamping: 0.2,
        initialSpringVelocity: 6.0,
        options: UIViewAnimationOptions.AllowUserInteraction,
        animations: { () -> Void in
            self.transform = CGAffineTransformIdentity
    }) { (Bool) -> Void in
        super.touchesCancelled(touches, withEvent: event)
    }

}


override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {

    UIView.animateWithDuration(0.5,
        delay: 0,
        usingSpringWithDamping: 0.2,
        initialSpringVelocity: 6.0,
        options: UIViewAnimationOptions.AllowUserInteraction,
        animations: { () -> Void in
            self.transform = CGAffineTransformIdentity
        }) { (Bool) -> Void in
            super.touchesEnded(touches, withEvent: event)
    }
  }
} 

这在快速点击时效果很好,但是当我长时间(1-2 秒)触摸按钮时,我没有得到内部动作事件的修饰。 当我将它切换回常规 UIButton 时,一切正常。

知道为什么会这样吗?

我没有在 touchesCancelledtouchesEnded 方法的完成块中调用 super,而是在其中调用了 self.sendActionsForControlEvents(UIControlEvents.TouchUpInside)

还不确定具体原因,但您需要在动画外调用 super.touchesEnded(touches, with: event)

所以 (Swift 5)

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    UIView.animate(withDuration: 0.5,
        delay: 0,
        usingSpringWithDamping: 0.2,
        initialSpringVelocity: 6.0,
        options: UIView.AnimationOptions.allowUserInteraction,
        animations: { () -> Void in
            self.transform = CGAffineTransform.identity
        }) { (Bool) -> Void in
    }
    super.touchesEnded(touches, with: event)
}

这是一种自 Swift 5 起有效的方法,因此 UIButton subclass 可以拦截其按钮推送,并将注册的 target/action 侦听器转发到按钮外部 class:

  1. 在子class编辑按钮的 UIView 超级视图中:

     button.addTarget(self, action: #selector(buttonPushed), 
        for: .primaryActionTriggered)
    
  2. 在按钮 subclass 中,通过将此添加到 init() 方法来检测按钮按下:

     let tapGestureRecognizer = UITapGestureRecognizer(target: self, 
            action: #selector(buttonPressed))
     tapGestureRecognizer.numberOfTapsRequired    = 1;
     tapGestureRecognizer.numberOfTouchesRequired = 1;
     self.addGestureRecognizer(tapGestureRecognizer)
    
  3. 使用处理程序在 subclass 内按下陷阱按钮:

     @objc func buttonPressed() {
        // .
        // . (do whatever subclass needs to)
        // .
        self.sendActions(for: .primaryActionTriggered)
     }
    

如果没有 sendActions,在 init 中添加的 gestureRecognizer 将阻止事件传播到 ResponderChain 中的消费者。