UITabBarItem Icon Animation

Keep in mind that the order of the subviews in the UITabBar may be unordered so accessing the correct item from it using an index may not work correctly as Naresh answer suggested. Have a look at this post UITabBar subviews change order

The solution I found is to first filter and sort the views and then access it using the correct index of the tabBarItem.

func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {

    let orderedTabBarItemViews: [UIView] = {
        let interactionViews = tabBar.subviews.filter({ $0 is UIControl })
        return interactionViews.sorted(by: { $0.frame.minX < $1.frame.minX })
    }()

    guard
        let index = tabBar.items?.firstIndex(of: item),
        let subview = orderedTabBarItemViews[index].subviews.first
    else {
        return
    }

    performSpringAnimation(for: subview)
}

func performSpringAnimation(for view: UIView) {
    UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
        view.transform = CGAffineTransform(scaleX: 1.25, y: 1.25)
        UIView.animate(withDuration: 0.5, delay: 0.2, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
            view.transform = CGAffineTransform(scaleX: 1, y: 1)
        }, completion: nil)
    }, completion: nil)
}

I have found a better solution to this problem. Adding custom image view is not a better approach because in iOS 10.0 and later on changing the orientation the icon frame & text position changed. You just need to set the class of UITabBarController & put the following code.

override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {

    let index = self.tabBar.items?.index(of: item)
    let subView = tabBar.subviews[index!+1].subviews.first as! UIImageView
    self.performSpringAnimation(imgView: subView)
}

//func to perform spring animation on imageview
func performSpringAnimation(imgView: UIImageView) {

    UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {

        imgView.transform = CGAffineTransform.init(scaleX: 1.4, y: 1.4)

        //reducing the size
        UIView.animate(withDuration: 0.5, delay: 0.2, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
            imgView.transform = CGAffineTransform.init(scaleX: 1, y: 1)
        }) { (flag) in
        }
    }) { (flag) in

    }
}  

I am surprised how easy the solution was!

Add method to your Application Delegate class .m-file (or any other class that manages your UITabBar) containing the following routine:

  1. Create an UIImageView that will be used for animation.
  2. Add it to your TabBar view using the addSubview: method.
  3. Frame it down to the size of UITabBarItem (use UITabBar frame size and the number of tab bar items to calculate the frame size).
  4. Adjust the imageView's frame.origin.x value to place the Image right above the tab bat item you want to animate.
  5. Add animation you want to the imageView (you can play with opacity, swap several images - anything you want).

Pretty easy, don't you think so?

You can call this method on UIApplicationDelegate instance anywhere you need to animate the tab bar item.

Also it is important to notice that you can tap THROUGH the imageView to select the tab bar item as if there was no image over the tab bar. Many interesting conclusions can be done here on what you can do if you know it...