Written by Alex Gibson, on April 3, 2017


Great apps have two things in common.  They have a great story and they do not feel stiff.  You can survive a while with a great story and of the two it is most important but apps with great stories eventually have to be playful with the users. Users are also becoming better judges of design.  Some may not know how to execute the design but they know what like.  Developers and designers are constantly having to step up the game to wow users.  One of the ways we have seen this happen is in micro interactions.  Normal animations are not enough as smaller items are being animated.  In this tutorial we are going to work on a relatively simple micro animation.  This will give your app something extra without much code, time, or overhead in your app.

Download the starter project from Github.

Run the application just to see what we are dealing with.  You should see an app with three tabs that we can select the tabbar items and change the view controllers.  All we are going to work on is adding some animation to the touch of the tabbar icon.  This will be a very short tutorial and that is great.  You will be able to add a small meaningful animation without much effort.

Go to File->New->File->Cocoa Touch Class and choose a subclass of a UITabbarController. Name it SpringyTabbarController.

At the top of the file let’s make it conform to UITabbarControllerDelegate


import UIKit

class SpringyTabbarController: UITabBarController,UITabBarControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

}

Now to intercept the taps on the icons we are going to use the delegate method func tab​Bar​Controller(UITab​Bar​Controller, did​Select:​ UIView​Controller) which as the docs say "Tells the delegate that the user selected an item in the tab bar.” So, let’s add this to our SpringyTabbarController and a method that we will name animateTabbarView().  We also need to set the delegate of the tabbarcontroller in the viewDidLoad.


import UIKit

class SpringyTabbarController: UITabBarController,UITabBarControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        delegate = self
    }
    
    func animateTabBarView(view:UIView){
        
    }
    
    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        print("This is a test")
    }

}

You can see that I added a log statement to the delegate method to make sure that is works.  Now go into the storyboard and change the UITabbarController to SpringyTabbarController

 

Now run the project and tap on the icons.  Make sure that the print statement is showing in the console. Now go back to the file SpringyTabbarController.

Our goal is to animate the tabbar icon and text so how do we get the one that is tapped.  You will find a lot of different answers to this and there are not many that I like.  The most popular is to tag each tabbar item so that you can identify it.  I would rather not have to do that so there has to be a better way and actually as it turns out there is and it is very Swifty.  We can sort tabbar items by frame origin to determine the order of the icons and the index in the tabbar.  We can also use the viewController passed in the delegate method to get it’s index.  This means if we know the order of the indexes of the tab items and the viewcontroller that was tapped we are in business.  We are going to add this new method that gets the order of our tab items and we are going to get the index from our delegate.  Using these two pieces of information we are going to have the tappedItem.


import UIKit

class SpringyTabbarController: UITabBarController,UITabBarControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        delegate = self
    }
    
    func animateTabBarView(view:UIView){
        
    }
    
    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        guard let index = tabBarController.viewControllers?.index(of: viewController) else{return}
        let tappedItem = orderedTabBarItemViews(tabBar: self.tabBar)[index]
        
    }
    
    //this checks the subviews of the tabbar and orders them for us.
    func orderedTabBarItemViews(tabBar:UITabBar) -> [UIView] {
        let interactionViews = tabBar.subviews.filter({$0.isUserInteractionEnabled})
        return interactionViews.sorted(by: {$0.frame.minX < $1.frame.minX})
    }
}

Now all we have left to do is animation.  Let’s try get straight to it.  Add this line the delegate function to pass our item to the function for animation.


animateTabBarView(view: tappedItem)

Now for the animation.  In the animateTabBarView function we need to animate it going down and back up.  We will write two animations to do this and give the rebound animation a delay that is equal to the down animations duration.  We do not want this to have a long duration.  Users can be impatient and we want the animation to just fit in with the tap.  Great animations are almost unnoticed at first.


func animateTabBarView(view:UIView){
        //animate down
        UIView.animate(withDuration: 0.1, animations: { 
            view.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
        }, completion: nil)
        
        //animate up
        UIView.animate(withDuration: 0.15, delay: 0.1, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.9, options: .curveEaseOut, animations: { 
            view.transform = .identity
        },completion:nil)
    
    }

Now run the project and you should see on a tap that the icon and text shrink and spring back. The animation is short but a nice touch.

 

Your final SpringyTabbarController.


import UIKit

class SpringyTabbarController: UITabBarController,UITabBarControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        delegate = self
    }
    
    func animateTabBarView(view:UIView){
        //animate down
        UIView.animate(withDuration: 0.1, animations: { 
            view.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
        }, completion: nil)
        
        //animate up
        UIView.animate(withDuration: 0.15, delay: 0.1, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.9, options: .curveEaseOut, animations: { 
            view.transform = .identity
        },completion:nil)
    
    }
    
    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        guard let index = tabBarController.viewControllers?.index(of: viewController) else{return}
        let tappedItem = orderedTabBarItemViews(tabBar: self.tabBar)[index]
        animateTabBarView(view: tappedItem)
    }
    
    //this checks the subviews of the tabbar and orders them for us.
    func orderedTabBarItemViews(tabBar:UITabBar) -> [UIView] {
        let interactionViews = tabBar.subviews.filter({$0.isUserInteractionEnabled})
        return interactionViews.sorted(by: {$0.frame.minX < $1.frame.minX})
    }
}

 

I hope you enjoyed this.  Feel free to play with the animation values.  You can find the final project on the Apptillery Github.