Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shake Animation for UITextField/UIView in Swift

I am trying to figure out how to make the text Field shake on button press when the user leaves the text field blank.

I currently have the following code working:

if self.subTotalAmountData.text == "" {

    let alertController = UIAlertController(title: "Title", message:
        "What is the Sub-Total!", preferredStyle: UIAlertControllerStyle.Alert)
    alertController.addAction(UIAlertAction(title: "Okay", style: UIAlertActionStyle.Default,handler: nil))

    self.presentViewController(alertController, animated: true, completion: nil)

} else {

}

But i think it would be much more appealing to just have the text field shake as an alert.

I can't find anything to animate the text field.

Any ideas?

Thanks!

like image 481
Joe CRONE Scrivens Avatar asked Jan 16 '15 15:01

Joe CRONE Scrivens


4 Answers

You can change the duration and repeatCount and tweak it. This is what I use in my code. Varying the fromValue and toValue will vary the distance moved in the shake.

let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.07
animation.repeatCount = 4
animation.autoreverses = true
animation.fromValue = NSValue(cgPoint: CGPoint(x: viewToShake.center.x - 10, y: viewToShake.center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: viewToShake.center.x + 10, y: viewToShake.center.y))

viewToShake.layer.add(animation, forKey: "position")
like image 141
rakeshbs Avatar answered Nov 20 '22 06:11

rakeshbs


The following function is used in any view.

extension UIView {
    func shake() {
        let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
        animation.duration = 0.6
        animation.values = [-20.0, 20.0, -20.0, 20.0, -10.0, 10.0, -5.0, 5.0, 0.0 ]
        layer.add(animation, forKey: "shake")
    }
}
like image 25
Surckarter Avatar answered Nov 20 '22 07:11

Surckarter


EDIT: using CABasicAnimation cause the app to crash if you ever trigger the animation twice in a row. So be sure to use CAKeyframeAnimation. Bug has been fixed, thanks to the comments :)


Or you can use this if you want more parameters (in swift 5) :

public extension UIView {

    func shake(count : Float = 4,for duration : TimeInterval = 0.5,withTranslation translation : Float = 5) {

        let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
        animation.repeatCount = count
        animation.duration = duration/TimeInterval(animation.repeatCount)
        animation.autoreverses = true
        animation.values = [translation, -translation]
        layer.add(animation, forKey: "shake")
    }  
}

You can call this function on any UIView, UIButton, UILabel, UITextView etc. This way

yourView.shake()

Or this way if you want to add some custom parameters to the animation:

yourView.shake(count: 5, for: 1.5, withTranslation: 10)
like image 41
RomOne Avatar answered Nov 20 '22 06:11

RomOne


I think all of these are dangerous.

If your shake animation is based on a user action and that user action is triggered while animating.

CRAAAAAASH

Here is my way in Swift 4:

static func shake(view: UIView, for duration: TimeInterval = 0.5, withTranslation translation: CGFloat = 10) {
    let propertyAnimator = UIViewPropertyAnimator(duration: duration, dampingRatio: 0.3) {
        view.transform = CGAffineTransform(translationX: translation, y: 0)
    }

    propertyAnimator.addAnimations({
        view.transform = CGAffineTransform(translationX: 0, y: 0)
    }, delayFactor: 0.2)

    propertyAnimator.startAnimation()
}

Maybe not the cleanest, but this method can be triggered repeatedly and is easily understood

Edit:

I am a huge proponent for usage of UIViewPropertyAnimator. So many cool features that allow you to make dynamic modifications to basic animations.

Here is another example to add a red border while the view is shaking, then removing it when the shake finishes.

static func shake(view: UIView, for duration: TimeInterval = 0.5, withTranslation translation: CGFloat = 10) {
    let propertyAnimator = UIViewPropertyAnimator(duration: duration, dampingRatio: 0.3) {
        view.layer.borderColor = UIColor.red.cgColor
        view.layer.borderWidth = 1
        view.transform = CGAffineTransform(translationX: translation, y: 0)
    }

    propertyAnimator.addAnimations({
        view.transform = CGAffineTransform(translationX: 0, y: 0)
    }, delayFactor: 0.2)

    propertyAnimator.addCompletion { (_) in
        view.layer.borderWidth = 0
    }

    propertyAnimator.startAnimation()
}
like image 27
Corey Pett Avatar answered Nov 20 '22 05:11

Corey Pett