'Subclassing UIButton and overriding touch events - not working
I need a scale spring animation for all my buttons in a project. So i subclassed UIButton and override touch event functions.
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)
}
}
}
this works great in a fast tap but when i touch the button for a long time (1-2 seconds) i don't get a touch up inside action event. when i switch it back to a regular UIButton everything works fine.
Any ideas why that happens?
Solution 1:[1]
Not sure exactly why yet, but you need to call super.touchesEnded(touches, with: event) outside the animation
So (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)
}
Solution 2:[2]
This is an approach that works as of Swift 5, so UIButton subclass can intercept its button push, and forward the registered target/action listeners external to the button class:
In the UIView superview of the subclassed button:
button.addTarget(self, action: #selector(buttonPushed), for: .primaryActionTriggered)In button subclass, detect button press by adding this to
init()method:let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(buttonPressed)) tapGestureRecognizer.numberOfTapsRequired = 1; tapGestureRecognizer.numberOfTouchesRequired = 1; self.addGestureRecognizer(tapGestureRecognizer)Trap button press inside the subclass with a handler:
@objc func buttonPressed() { // . // . (do whatever subclass needs to) // . self.sendActions(for: .primaryActionTriggered) }
Without sendActions, the gestureRecognizer that was added in init will prevent the event from being propagated to the consumers in the ResponderChain.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Arnaud |
| Solution 2 | clearlight |
