12

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?

luk2302
  • 55,258
  • 23
  • 97
  • 137
ilan
  • 4,402
  • 6
  • 40
  • 76
  • where not otherwise specified by the documentation the first thing to do in an overridden method is to call the super. It's a little bit strange call it inside an animation block. – LuckyStarr Jun 28 '15 at 22:09
  • if i call super before the animation ended, the animation will not animate. Do you have any way to make this animation? – ilan Jun 28 '15 at 22:18
  • 1
    Instead overriding the UIGestureRecognizer methods try to use the methods `beginTrackingWithTouch:withEvent:`, `continueTrackingWithTouch:withEvent:` and `endTrackingWithTouch:withEvent:` of the UIControl class of which the UIButton is a subclass. – LuckyStarr Jun 28 '15 at 23:12
  • this doesn't work... the animation is not being animated – ilan Jun 29 '15 at 06:01

3 Answers3

9

Instead of calling super in the completion block of touchesCancelled and touchesEnded methods I called self.sendActionsForControlEvents(UIControlEvents.TouchUpInside) in there.

LuckyStarr
  • 1,468
  • 2
  • 26
  • 39
ilan
  • 4,402
  • 6
  • 40
  • 76
2

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)
}
Arnaud
  • 17,268
  • 9
  • 65
  • 83
0

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:

  1. In the UIView superview of the subclassed button:

     button.addTarget(self, action: #selector(buttonPushed), 
        for: .primaryActionTriggered)
    
  2. 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)
    
  3. 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.

clearlight
  • 12,255
  • 11
  • 57
  • 75