Giving life to empty states

Giving life to empty states

Empty screens don't have to be boring, we can add details to create a moment that is engaging, welcoming and delight. For this we can use different types of resources that UIKit provides us.

RPReplay_Final1649991311.gif

Parallax effect on motion

To implement Parallax Effect motion, we need to use UIMotionEffect, which is a class that defines motion-based modifiers for views. We use UIInterpolatingMotionEffect for adding those effects to the view, and we need to assign appropriate values to the minimumRelativeValue and maximumRelativeValue properties. As the user moves the device, the motion effect started translating the fixed offset values returned by the system to the range of values specified.

func addMotionEffect() {
    let min = CGFloat(-30)
    let max = CGFloat(30)
    let xMotion = UIInterpolatingMotionEffect(keyPath: "layer.transform.translation.x",
                                              type: .tiltAlongHorizontalAxis)
    xMotion.minimumRelativeValue = min
    xMotion.maximumRelativeValue = max
    let yMotion = UIInterpolatingMotionEffect(keyPath: "layer.transform.translation.y",
                                              type: .tiltAlongVerticalAxis)
    yMotion.minimumRelativeValue = min
    yMotion.maximumRelativeValue = max
    let motionEffectGroup = UIMotionEffectGroup()
    motionEffectGroup.motionEffects = [xMotion, yMotion]
    view.addMotionEffect(motionEffectGroup)
}

Randomized animations

To create random animations we are going to help ourselves with UIViewPropertyAnimator; a class that animates changes to views and allows dynamic modification of those animations.

We will define a range for both the scale and translation of the view. Once the initial animation finishes we will call the animation that will be in charge of resetting the scale and the translation and then starting the animation again

 func randomizedAnimation(_ view: UIView ) {
     let completionAnimation = UIViewPropertyAnimator(duration: 1.5, curve: .easeInOut) {
          view.transform = .identity
     }
     completionAnimation.addCompletion { _ in self.randomizedAnimation(view) }
     let iconAnimation = UIViewPropertyAnimator(duration: 1.5, curve: .easeInOut) {
     let scale = CGFloat.random(in: 0.95...1.05)
     view.transform = CGAffineTransform(scaleX: scale, y: scale).translatedBy(x: CGFloat.random(in: -5...5),
                                                                              y: CGFloat.random(in: -5...5))
     }
     iconAnimation.addCompletion { _ in completionAnimation.startAnimation() }
     iconAnimation.startAnimation()
}

Bounce animation

To add a bounce effect to the views, we can also use the UIViewPropertyAnimator, here we will first add a UITapGesture to the view

view.addGestureRecognizer(UITapGestureRecognizer(target: self,
                                                 action: #selector(tapView(_:))))

Note: If the gesture is added to a UIImageView, remember to set isUserInteractionEnabled to true, since in the case of UIImageView the default value is false

Once the gesture is added we simply create an animation that scales the view and once the animation is finished we return to its normal scale

func bounceView(_ view: UIView) {
    let bounceAnimation = UIViewPropertyAnimator(duration: 0.25, curve: .easeInOut) {
         view.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)
    }
    bounceAnimation.addCompletion { _ in
         UIViewPropertyAnimator(duration: 0.25, curve: .easeInOut) {
              view.transform = .identity
         }.startAnimation()
    }
    bounceAnimation.startAnimation()
 }

We can create haptics to simulate physical impacts, for this we use the UIImpactFeedbackGenerator class and add it to the tap of the view

 let feedbackGenerator = UIImpactFeedbackGenerator(style: .light)
 feedbackGenerator.impactOccurred()