Creating UIView Animations to Make Your App Feel Polished

Animations can be the final touch that make your app feel polished and complete. But getting animations right can sometimes be tricky. You want to avoid animations for the sake of having an animation, and you also want to avoid jarring animations that just leave the user feeling disoriented and confused. Animations should be subtle and give the user a hint of what just happened, or what possible actions they can and cannot take. A great example of this is the animations in the new iOS 11 App Store app – particularly on the Today tab. Pushing down on a cell gently makes the size smaller as if you’re pushing it down, and releasing your finger provides a beautiful transition into the story view controller. The user is fully aware of the context of what just happened, as it looks great too.

ezgif-5-8f29a64c18

You’ll notice also that the animations don’t at all feel robotic and instead feel very natural and glide well. So today, let’s take a look at creating various UIView animations to get a better understanding of how they can be done.

First let’s take a look at that push in animation you get when you tap a cell on the Today view. This can be accomplished very easily by just creating a simple UIView.animate(withDuration:…) block and changing the transform property on the view’s layer.

Screen Shot 2017-11-30 at 11.03.49.png

To revert, we can create a new method that just resets the layer’s transform

Screen Shot 2017-11-30 at 11.05.47.png

Now what you can also do is adjust that first tapInAnimate() function to call tapOutAnimated() in its completion if shouldRevert is true.

Now let’s move onto something more subtle that’s actually been around a while – parallax. The effect here is the same one that you get on icons on your home screen when you move your phone. This method creates a UIMotionEffectGroup and adds it to the view.

Screen Shot 2017-11-30 at 11.11.11.png

Now just call yourView.addParalaxEffect() and when you start moving your phone around, you’ll get a subtle parallax effect added.

There are a few more, including a wobble animation and ‘tweak’ (I couldn’t think of a better word) animation that feels like when you hit a question block in a 2D Mario game. You can check them all out in the file I’ve linked to at the bottom of this post.

You can also apply these effects to a cell in a UICollectionView, and the property UICollectionView$bringSubview(toFront: UIView) might be useful in making the animations not clip underneath any other cells in the collection view.

Happy Christmas! ❄️🎅☃️


//
// Created by Jordan.Dixon on 30/11/2017.
// Copyright © 2017 Jordan.Dixon. All rights reserved.
//
import UIKit
// MARK: Paralax effect
internal extension UIView {
func addParalaxEffect(amount: Int = 20) {
let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
horizontal.minimumRelativeValue = -amount
horizontal.maximumRelativeValue = amount
let vertical = UIInterpolatingMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis)
vertical.minimumRelativeValue = -amount
vertical.maximumRelativeValue = amount
let group = UIMotionEffectGroup()
group.motionEffects = [horizontal, vertical]
addMotionEffect(group)
}
}
// MARK: Tweak
internal extension UIView {
internal enum TweakDirection {
case up, down, left, right
}
func tweak(offset: CGFloat = 30, inDirection direction: TweakDirection = .down) {
let cellAnimation = createTweakAnimation(for: self, to: offset, direction: direction)
self.layer.add(cellAnimation, forKey: "position")
}
private func createTweakAnimation(for view: UIView, to offset: CGFloat, direction: TweakDirection) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.1
animation.repeatCount = 0
animation.autoreverses = true
animation.fromValue = CGPoint(x: view.center.x, y: view.center.y)
switch direction {
case .up:
animation.toValue = CGPoint(x: view.center.x, y: view.center.y – offset)
case .down:
animation.toValue = CGPoint(x: view.center.x, y: view.center.y + offset)
case .left:
animation.toValue = CGPoint(x: view.center.x – offset, y: view.center.y)
case .right:
animation.toValue = CGPoint(x: view.center.x + offset, y: view.center.y)
}
return animation
}
}
// MARK: Wobble
internal extension UIView {
func wobble(duration: CFTimeInterval = 0.07, repeatCount: Float = 3) {
let animation = CAKeyframeAnimation(keyPath: "transform")
let wobbleAngle: CGFloat = 0.09
let valLeft = CATransform3DMakeRotation(wobbleAngle, 0, 0, 1)
let valRight = CATransform3DMakeRotation(-wobbleAngle, 0, 0, 1)
animation.values = [valLeft, valRight]
animation.autoreverses = true
animation.duration = duration
animation.repeatCount = repeatCount
layer.add(animation, forKey: "transform")
}
}
// MARK: Tap In/Out
internal extension UIView {
func tapInAnimated(shouldRevert: Bool = true, duration: TimeInterval = 0.2) {
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut, .allowUserInteraction], animations: {
self.layer.transform = CATransform3DMakeScale(0.8, 0.8, 0.8)
}, completion: { didAnimate in
if shouldRevert { self.tapOutAnimated() }
})
}
func tapOutAnimated(duration: TimeInterval = 0.5, withDelay delay: Double = 0) {
let damping: CGFloat = delay == 0 ? 1.0 : 0.5
UIView.animate(withDuration: duration, delay: delay, usingSpringWithDamping: damping, initialSpringVelocity: damping, options: [.curveEaseOut, .allowUserInteraction], animations: {
self.layer.transform = CATransform3DIdentity
}, completion: nil)
}
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s