Animator: easy trick to make UIKit animations reusable

Fighting duplicate animation code in two easy steps

People sometimes come to me on the street and ask:

And whenever that happens, I answer “oh boy, what a silly question. Everybody know that those are and !”.

Incredible, huh?

So in this post, I’ll try to explain how we can make those two things work very nicely together.

The Problem

Let’s say after years of experimentation, you finally came up with an absolutely perfectly tweaked spring animation for your app using nothing but default UIView.animate parameters. Let’s say, hypothetically, that the code is something like this:

UIView.animate(
withDuration: 0.4,
delay: 0,
usingSpringWithDamping: 0.8,
initialSpringVelocity: 1.75,
options: [.curveEaseInOut, .beginFromCurrentState, .allowUserInteraction]
) {
self.view.center.y += 50
} completion: { (isCompleted) in
// completion code
}

Well, if you absolutely nail it and you want to start using this animation everywhere in your app, you suddenly start seeing yourself copying and pasting this snippet everywhere. And we both know that copy-pasted code is no good.

Many people try to solve this by creating a centralized “constants” type, where one can store their perfectly-tuned parameters and quickly grab them when needed. Something like this:

UIView.animate(
withDuration: AnimationDefaults.defaultDuration,
delay: 0,
usingSpringWithDamping: AnimationDefaults.defaultSpringDamping,
initialSpringVelocity: AnimationDefaults.defaultSpringVelocity,
options: AnimationDefaults.defaultOptions
) {
self.view.center.y += 50
} completion: { (isCompleted) in
// completion code
}

But while this is better than copy-pasted code, it’s still the same. Cause if one day you’ll want to migrate your animations to UIViewPropertyAnimator, well… you’ll have yourself a problem.

The Solution: “Animator” struct

Instead, here’s what we came up here at Nice Photon. First, let’s create a new type called Animator:

struct Animator {
typealias Animations = () -> ()
typealias Completion = (Bool) -> ()

let perform: (@escaping Animations, Completion?) -> ()
}

And with that, a simple extension on a UIView to mirror the usual UIView.animate as closely as possible:

extension UIView {
static func animate(with animator: Animator, animations: @escaping () -> (), completion: ((Bool) -> ())? = nil) {
animator.perform(animations, completion)
}
}

I hope by now you understand where we’re going with this. Now adding a reusable, perfectly tuned spring animation is simple:

extension Animator {
static let defaultSpring = Animator { (animations, completion) in
UIView.animate(
withDuration: 0.4,
delay: 0,
usingSpringWithDamping: 0.8,
initialSpringVelocity: 1.75,
options: [.curveEaseInOut, .beginFromCurrentState, .allowUserInteraction],
animations: animations,
completion: completion
)
}
}

So as you see, we’re simply wrapping our spring animation in a reusable block of code. The usage?

UIView.animate(with: .defaultSpring) {
self.view.center.y += 50
} completion: { (isCompleted) in
// completion code
}

Now it’s not just reusable, but also incredibly clean and easy to read.

With this technique, you can create as many reusable animations as you want.

One More Thing

Often, you want your methods that perform animations to have an animated: Bool parameter. Well, you probably remember how much of a pain that is with custom animations, right? Fret no more, here’s a neat little trick using the Animator:

extension Animator {
static let noAnimation = Animator { (animations, completion) in
animations()
completion?(true)
}
}

And the usage:

func moveView(animated: Bool) {
UIView.animate(with: animated ? .defaultSpring : .noAnimation) {
self.view.center.y += 50
} completion: { (isCompleted) in
// completion code
}
}

Hopefully, you find this as cool as I do.

Nice Photon is available for hire! Talk to us if you have any iOS app development needs. We have 10+ years of experience making iOS apps for top Silicon Valley companies. Reach out at hi@nicephoton.com

iOS development know-it-all, Co-founder at nicephoton.com. Talk to me about Swift, coffee, photography & motorsports.