I am the author of iOS 17 Fundamentals, Building iOS User Interfaces with SwiftUI, and eight other courses on Pluralsight.
Deepen your understanding by watching!
Fade In / Out Animations as Class Extensions in Swift
Updated on December 6, 2016 – Xcode 8 & Swift 3.0
The question has been asked (and solved) on StackOverflow in Objective-C, but my aim in this post is to take the Objective-C implementation and leverage Swift extensions to make this job even easier to achieve and reuse.
Fade animations basically involve adjusting a UIView
‘s alpha value from 1.0 to 0.0 (fade out) or 0.0 to 1.0 (fade in) over a specified duration using some kind of easing option (like starting fast, then slowing down at the end of the animation, or starting slow and speeding up at the end of the animation).
I’ve published an example Xcode project to GitHub with the final working version of the code below if you’d like to just see it. Read on for the full explanation.
Edit: 2/23/2016 – A new idea flowing out of my Pluralsight Course involves a similar implementation, but using protocol extensions instead. This article’s implementation still works though, so feel free to check out either the contents of this blog entry, or the new one!
Fade without an extension
Below is an example of how my view controller may look if I want to click a button and have it fade out a label, set the text, and fade it back in again:
1class ViewController: UIViewController {
2 @IBOutlet weak var birdTypeLabel: UILabel!
3
4 override func viewDidLoad() {
5 super.viewDidLoad()
6 // Do any additional setup that your app requires
7 }
8
9 @IBAction func updateBirdTypeLabel(_ sender: UIButton) {
10 // Fade out to set the text
11 UIView.animate(withDuration: 1.0, delay: 0.0, options: UIViewAnimationOptions.curveEaseOut, animations: {
12 self.birdTypeLabel.alpha = 0.0
13 }, completion: {
14 (finished: Bool) -> Void in
15
16 //Once the label is completely invisible, set the text and fade it back in
17 self.birdTypeLabel.text = "Bird Type: Swift"
18
19 // Fade in
20 UIView.animate(withDuration: 1.0, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
21 self.birdTypeLabel.alpha = 1.0
22 }, completion: nil)
23 })
24 }
25}
What I don’t like about this implementation is that if I want to perform this same kind of animation again elsewhere in my app, I’ve got to write the bulk of that algorithm again each time I want to fade something in or out. I’d like it to be in one place for easier maintainability. I’d also like to be able to fade in / out simply by doing something like self.birdTypeLabel.fadeIn()
or self.birdTypeLabel.fadeOut()
optionally setting parameters for duration, delay, and completion. With these goals in mind, let’s see what Swift extensions provide us in terms of simplifying the process.
Refactoring using Swift extensions
Step 1 – Create UIViewExtensions.swift
Create a new Swift file and name it something like UIViewExtensions.swift
Step 2 – Move fadeOut and fadeIn to UIViewExtensions.swift
Use the previously-written fadeOut()
and `fadeIn() algorithms in the new UIViewExtensions.swift file.
We can leverage what we wrote before with a few modifications. Take a look (I’ve written some comments to help identify some of the tweaks for the extension version):
1import Foundation
2import UIKit
3
4extension UIView {
5 func fadeIn() {
6 // Move our fade out code from earlier
7 UIView.animate(withDuration: 1.0, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
8 self.alpha = 1.0 // Instead of a specific instance of, say, birdTypeLabel, we simply set [thisInstance] (ie, self)'s alpha
9 }, completion: nil)
10 }
11
12 func fadeOut() {
13 UIView.animate(withDuration: 1.0, delay: 0.0, options: UIViewAnimationOptions.curveEaseOut, animations: {
14 self.alpha = 0.0
15 }, completion: nil)
16 }
17}
With this extension in place, we can now call self.birdTypeLabel.fadeIn()
or self.birdTypeLabel.fadeOut()
. To gain a little more control (if I so choose), I can outfit the fadeIn
and fadeOut
extension functions with parameters with default values defined so that I can call them with or without parameters as I need.
Step 3 – Provide parameters with default values
In Step 2, we simply hard-coded values for duration, delay, and completion. Below is the final version of the extension that provides parameters for you to (optionally) pass arguments to.
1import Foundation
2import UIKit
3
4extension UIView {
5 func fadeIn(_ duration: TimeInterval = 1.0, delay: TimeInterval = 0.0, completion: @escaping ((Bool) -> Void) = {(finished: Bool) -> Void in}) {
6 UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: {
7 self.alpha = 1.0
8 }, completion: completion) }
9
10 func fadeOut(_ duration: TimeInterval = 1.0, delay: TimeInterval = 0.0, completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in}) {
11 UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: {
12 self.alpha = 0.0
13 }, completion: completion)
14 }
15}
With this now in place, the final version of my view controller becomes much simpler and clean:
1import UIKit
2
3class ViewController: UIViewController {
4 @IBOutlet weak var birdTypeLabel: UILabel!
5
6 override func viewDidLoad() {
7 super.viewDidLoad()
8 // Do any additional setup that your app requires
9 }
10
11 @IBAction func updateBirdTypeLabel(_ sender: UIButton) {
12 self.birdTypeLabel.fadeOut(completion: {
13 (finished: Bool) -> Void in
14 self.birdTypeLabel.text = "Bird Type: Swift"
15 self.birdTypeLabel.fadeIn()
16 })
17 }
18}
By employing Swift extensions to encapsulate the fade in / out animation logic, I was able to
- Define the animation logic in one place for easy maintainability
- Make my view controller’s code simpler and clean
- Provide a more natural way to perform the animation on any UIView instance by simply calling fadeIn() or fadeOut()
- Give myself the option to specify a different duration, delay, or completion closure if I need extra control