Slide In Animation in Swift

In a previous post about fade animations in Swift, I demonstrated how to use a class extension to add the ability for any UIView instance to easily call fadeIn() or fadeOut() on itself. This strategy was nice – the animations, while simple, would have cluttered my code each time I used them, had I not encapsulated them somewhere. Employing class extensions in Swift seemed a natural way to provide this functionality to UIViews.

Well, I liked the idea so much that when it came time for me to implement a slide animation, I kept the same strategy, and I’d like to share my implementation with you today.

Animation demo

To start off with, here’s a sample of what the animation does:

Slide Animation Example

The easiest way to get the gist of what I’ve done is to head over to GitHub and download the example Xcode Project. In real life, I’ve combined the fade animations and the slide animation into the same UIView extension, but for education’s sake, I’ve split them out so you can easily see the moving parts.

Implementation overview

There are three major parts to this example implementation, which I’ll explain in detail shortly. Here’s the overview:

1 – Create the UIView extension

 1import UIKit
 2
 3extension UIView {
 4    // Name this function in a way that makes sense to you... 
 5    // slideFromLeft, slideRight, slideLeftToRight, etc. are great alternative names
 6    func slideInFromLeft(duration: NSTimeInterval = 1.0, completionDelegate: AnyObject? = nil) {
 7        // Create a CATransition animation
 8        let slideInFromLeftTransition = CATransition()
 9        
10        // Set its callback delegate to the completionDelegate that was provided (if any)
11        if let delegate: AnyObject = completionDelegate {
12            slideInFromLeftTransition.delegate = delegate
13        }
14        
15        // Customize the animation's properties
16        slideInFromLeftTransition.type = kCATransitionPush
17        slideInFromLeftTransition.subtype = kCATransitionFromLeft
18        slideInFromLeftTransition.duration = duration
19        slideInFromLeftTransition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
20        slideInFromLeftTransition.fillMode = kCAFillModeRemoved
21        
22        // Add the animation to the View's layer
23        self.layer.addAnimation(slideInFromLeftTransition, forKey: "slideInFromLeftTransition")
24    }
25}

2 – Set up the storyboard

  • From the Utilities panel, drag over a regular View to act as a wrapper for the sliding text (use a regular View, not a Container View)
  • From the Utilities panel, drag a Label into the wrapper view and create an IBOutlet to your View Controller for the Label
  • Set up auto layout constraints for both the wrapper and the Label
  • Set the wrapper view’s Clip Subviews property to checked in the Attributes Inspector

3 – Code the View Controller – initiate slide animation.

In my example, I wired the trigger up to a button’s touchUpInside action. For you, it may be something different that triggers the animation to begin. Whatever it may be, call slideInFromLeft() on your UIView instance (in my case, the UILabel).

1@IBAction func slideTextButtonTapped(sender: UIButton) {
2        self.slidingTextLabel.slideInFromLeft()
3        self.slidingTextLabel.text = "Sliding Text!"
4    }

Notice that directly after the call to slideInFromLeft(), I change the label’s text property to contain the new text that I want to slide in.

Implementation details

The real work all happens inside the UIViewExtensions.swift file where I add the slideInFromLeft() function to a UIView’s arsenal.`

UIViewExtensions.swift

First to note is that I’ve provided a few default values in the function’s signature so that the animation can be initiated as simply as writing viewInstance.slideInFromLeft(), or as “complicated” as providing argument values to both duration and completionDelegate:

1extension UIView {
2    func slideInFromLeft(duration: NSTimeInterval = 1.0, completionDelegate: AnyObject? = nil) {
3    // Implementation...
4}

Next, I create a CATransition instance, and set its delegate property if a completionDelegate is passed when the function is called:

1extension UIView {
2    func slideInFromLeft(duration: NSTimeInterval = 1.0, completionDelegate: AnyObject? = nil) {
3        let slideInFromLeftTransition = CATransition()
4        
5        if let delegate: AnyObject = completionDelegate {
6            slideInFromLeftTransition.delegate = delegate
7        }
8            // Remaining implementation...
9}

I then go about configuring the animation’s properties. To achieve the “slide in from left” animation, I set the type and the subtype properties to kCATransitionPush and kCATransitionFromLeft, respectively. These two combined create the “slide in” effect. Other properties that I set are duration, timingFunction, and fillMode:

 1extension UIView {
 2        // ...
 3        slideInFromLeftTransition.type = kCATransitionPush
 4        slideInFromLeftTransition.subtype = kCATransitionFromLeft
 5        slideInFromLeftTransition.duration = duration
 6        slideInFromLeftTransition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
 7        slideInFromLeftTransition.fillMode = kCAFillModeRemoved
 8        
 9        // ...
10    }
11}

To keep things simple, I only allow myself to customize the duration property, and optionally provide a completionDelegate when I call the function… the other properties are more fundamental to how the animation should perform, so I encapsulate the implementation here so that it’s an abstraction when I actually call slideInFromLeft() later in my View Controller.

The last thing I do is add the animation that I just created and configured to the view’s layer property. “The view” here would be the instance of UIView that has calls slideInFromLeft():

1extension UIView {
2        // ...
3        self.layer.addAnimation(slideInFromLeftTransition, forKey: "slideInFromLeftTransition")
4    }
5}

Storyboard setup details

This is probably the more challenging piece, just because it takes some tweaking to get the auto layout constraints just right.

  • My general setup in this example is a Label wrapped inside a containing UIView. I made sure to simply drag over a regular View (not a Container View) from the utilities pane on the right. I then dragged a Label inside the “wrapper” view.
  • I set wrapper view’s width to a value that was less than the entire screen’s width. This was so that the sliding view didn’t appear to slide in from off-screen and slide out off-screen. Instead it appears to slide in from underneath the wrapper view… You’re welcome to customize this how you want (or avoid the containing view altogether if you find it’s not needed for your specific implementation).
  • For the wrapper view, I’ve set constraints for
    • Center Horizontally in Container
    • Top Space to Top Layout Guide
    • Width
    • Height

Wrapper View Constraints

Next, I made sure that my label that will be sliding is inside the wrapper view. I set its constraints to

  • Center Horizontally in Container
  • Center Vertically in Container
  • Equal Widths
  • Equal Heights

Label Constraints

The last thing I do in the storyboard before wiring things in the View Controller is to select the wrapper view and make sure it’s Clip Subviews property is checked. As an experiment, toggle this option and watch the effect it has on the animation’s appearance:

Clib Subviews Checked

View controller implementation

The final piece of the setup is to wire things up to the View Controller and animate the Label.

Since I needed to reference the Label containing the text that I’d like to animate, I created an IBOutlet from my storyboard to my View Controller:

1class ViewController: UIViewController {
2    @IBOutlet weak var slidingTextLabel: UILabel!
3    // ...
4}

Next, I needed a way to initiate the slide in animation – I decided that wiring it to a button’s touchUpInside action would be sufficient for the example, so I created an IBAction from my Storyboard to my View Controller for that purpose.

Within the body of that IBAction, I wrote the call to slideInFromLeft() on my slidingTextLabel instance:

 1import UIKit
 2
 3class ViewController: UIViewController {
 4    // ...
 5    @IBAction func slideTextButtonTapped(sender: UIButton) {
 6        self.slidingTextLabel.slideInFromLeft()
 7//      self.slidingTextLabel.slideInFromLeft(duration: 1.0, completionDelegate: self) // Use this line to specify a duration or completionDelegate
 8        self.slidingTextLabel.text = "Sliding Text!"
 9    }
10    
11    // ...
12}

If you have need to specify a duration or a completionDelegate, there’s a commented out line of code there that shows an example of passing those arguments to the slideInFromLeft() function.

If you specify a completionDelegate to the slideInFromLeft() function, a method called animationDidStop() will be called when the animation finishes. Inside this callback function, you can write code to perform any action you’d like to have happen after the animation has finished. If you don’t set a completionDelegate, there’s no need to have this method override in your code:

1override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
2        println("Animation stopped")
3    }

That’s a wrap, folks! Hope this strategy is helpful for you as you think about where to place code for your UIView animations.

comments powered by Disqus