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

comments powered by Disqus