Animation can vastly improve user experience in an application. I love buttons that animate and give you the feeling that you are actually pressing something. These combined with the great TapticEngine APIs (UIFeedbackGenerator) can completely change the way your application feels.

Here are a couple of ways of doing button animations in Swift - both of which utilise UIButton addTarget methods.

Vanilla UIKit Method

extension UIButton { func startAnimatingPressActions () { addTarget ( self , action : #selector( animateDown ) , for : [ . touchDown , . touchDragEnter ]) addTarget ( self , action : #selector( animateUp ) , for : [ . touchDragExit , . touchCancel , . touchUpInside , . touchUpOutside ]) } @objc private func animateDown ( sender : UIButton ) { animate ( sender , transform : CGAffineTransform . identity . scaledBy ( x : 0.95 , y : 0.95 )) } @objc private func animateUp ( sender : UIButton ) { animate ( sender , transform : . identity ) } private func animate ( _ button : UIButton , transform : CGAffineTransform ) { UIView . animate ( withDuration : 0.4 , delay : 0 , usingSpringWithDamping : 0.5 , initialSpringVelocity : 3 , options : [ . curveEaseInOut ], animations : { button . transform = transform }, completion : nil ) } }

If you are not using RxSwift or RxCocoa, this method should work just as well. The only downside is that you once a button becomes animatable, you have no way of making it un-animatable.

RxSwift + RxCocoa

This is my preferred method of making generic animations. The benefit of using RxSwift is that you start and stop animating button presses - should you wish to - simply by disposing the DisposeBag that you pass in. It also avoids you having to @objc expose your control event methods.

import RxSwift import RxCocoa extension UIButton { func animateWhenPressed ( disposeBag : DisposeBag ) { let pressDownTransform = rx . controlEvent ([ . touchDown , . touchDragEnter ]) . map ({ CGAffineTransform . identity . scaledBy ( x : 0.95 , y : 0.95 ) }) let pressUpTransform = rx . controlEvent ([ . touchDragExit , . touchCancel , . touchUpInside , . touchUpOutside ]) . map ({ CGAffineTransform . identity }) Observable . merge ( pressDownTransform , pressUpTransform ) . distinctUntilChanged () . subscribe ( onNext : animate ( _ :)) . disposed ( by : disposeBag ) } private func animate ( _ transform : CGAffineTransform ) { UIView . animate ( withDuration : 0.4 , delay : 0 , usingSpringWithDamping : 0.5 , initialSpringVelocity : 3 , options : [ . curveEaseInOut ], animations : { self . transform = transform }, completion : nil ) } }