Shake animation

07 February 2020

Often we need to make alerts to show that a particular text field that is required is empty when a button is tapped, like in the login or registration screens. This is a hassle because on almost any app that we are developing we have localization that we need to handle as well. One day a designer that I worked with came up with the idea that we can shake that field or buttons that are disabled. Maybe he saw it somewhere but I really liked it. Here is how to do it with an extension of CALayer so we can use it on any component in our apps. First we will start by adding an enum to that extension so we can add more animations in the future:

import UIKit

// MARK: - Animations
extension CALayer {

    enum AnimationKey: String {

        /// Key for the shine animation.
        case shake = "shakeAnimation"

    }

}

Then we can add the animation as a method in the CALayer extension:

/// Starts an animation that imitates shaking.
func shake() {
    let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
    animation.timingFunction = CAMediaTimingFunction(name: .linear)
    animation.duration = 0.6
    animation.values = [-20, 20, -20, 20, -10, 10, -5, 5, 0]

    add(animation, forKey: AnimationKey.shake.rawValue)
}

What this does is make a CAKeyframeAnimation which will move the layer on the x axis several times. The positions that the lawyer will take on the x axis are included in this array [-20, 20, -20, 20, -10, 10, -5, 5, 0], which are valued relative to the current position of the layer. The whole animation is going to take 0.6 seconds. And at least we are adding the animation to the layer.

Finally, how to use it. Lets for example, imagine that we have an emailTextField, passwordTextField and a loginButton. The user fills his email address and taps on the loginButton. The passwordTextField is empty, so we will call this to trigger the shake animation:

passwordTextField.layer.shake()

Full snippet:

// Created by © 2020 SwiftEverywhere. Can be used free of charge.

import UIKit

// MARK: - Animations
extension CALayer {

    enum AnimationKey: String {

        /// Key for the shine animation.
        case shake = "shakeAnimation"

    }

    /// Starts an animation that imitates shaking.
    func shake() {
        let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
        animation.timingFunction = CAMediaTimingFunction(name: .linear)
        animation.duration = 0.6
        animation.values = [-20, 20, -20, 20, -10, 10, -5, 5, 0]

        add(animation, forKey: AnimationKey.shake.rawValue)
    }
}