Build an Animated Custom Tab Bar in Your iOS Apps

Create beautiful iOS designs

Jędrzej Chołuj
Better Programming

--

Every UI designer loves beautiful and animated Tab Bars. On the other hand, it is a nightmare for a developer, who has to implement that design. For sure it will be easier to use Apple’s native Tab Bar component, and focus on the more interesting things, like business logic implementation. But what if we have to create a custom Tab Bar? Where to start?

I will try to answer those questions in this article. We will go through the most important aspects of creating a custom Tab Bar. The final effect will be an animated, easy to expand, fully customized Tab Bar, which I hope will save your time in the future, and make implementing the designer's dreamed Tab Bar faster, and more comfortable. Are you ready? Let’s go!

First things first, the presented example uses some dependencies. Thanks to them it was faster to implement the solution described in this article. Feel free to replace them with your native code, or other libraries. Those dependencies are:

  • SnapKit for the layout purposes
  • RxGesture for handling tapping on Tab Bar items
  • RxSwift for informing the TabBarController that item has been tapped

Now we can move to the implementation part!

Tab Bar Item

The item component is consisting of two different parts coupled together: the view layer and the enum handling all defined item types. Why enumeration type instead of a simple struct?

That’s easy, enums are powerful types in programming, and especially in the Swift language. They are gathering familiar cases together, are case iterable, can be extended to expand information about a specific case, or can use computed properties inside them.

Furthermore, enums are value types the same as structs. Pretty cool, huh?

As you can see CustomTabItem has properties for an icon, a selected icon, a name, and an associated viewController. That’s all, each value is defined using a switch statement, returning a specific value for each enum case.

The second part is the view layer of the Tab Bar Item. It is directly coupled with CustomTabItem, as an item is one of two arguments passed to the view during initialization. The second one is an index, unique for each view, it will be used later for changing a selectedIndex of Tab Bar.

Moreover, each view has a simple transition animation. The design of items is simple, consisting of an icon at the top, and a title below it. Based on the fact if an item is selected or not, the title can change into a rounded line below an icon.

The auto-layout and configuring view properties is not rocket science, so I will omit that part intentionally. If you are curious how I implemented that part, check the GitHub repository mentioned at the end of the article.

As our Item is already done we can move to the main view — the Tab Bar.

Tab Bar implementation

Let’s talk about the main component responsible for gathering all Tab Bar items together and handling the selection of them. Auto-layout is effortless as it is created as a subclass of the UIStackView component. The only thing to do is to add our items using addArrangedSubview(_ view: UIView). That’s handled inside thesetupHierarchy() method.

In the Extensions.swift file in the GitHub repository, you can find some useful extensions, like the one for adding many subviews to UIStackView in just one line of code.

Furthermore, for each item that we want to add to our Tab Bar, we have to set translatesAutoresizingMaskIntoConstraints to false to prevent the automatic creation of auto-layout for them, and clipsToBounds to true to clip our items to the bound of the Tab Bar. We will implement that part among other properties configurations, inside thesetupProperties() method.

Besides Tab Bar items declarations, we have to add the Observable sequence property responsible for emitting the selected item index.

As you can see above, the subject itself is declared as a private constant, and there is an exposed Observable on the value of the subject.

Due to that, we ensure that nobody will be able to mess up values emitted by this subject from outside of the CustomTabBar class.

The last but not least, we have to add two methods two our CustomTabBar implementation. The first one is selectItem(index: Int), here we will handle the whole logic responsible for updating the currently selected item.

Firstly, we update the isSelected property in each of our items, to reflect the newest selection.

Secondly, we emit the index of the selected item using the subject described above. In that way, we update selection in both — TabBarController and Tab Bar items.

The second method which we have to implement is a bind(), responsible for handling user touches for each of our items.

The implementation is using RxGesture, but if you want you can replace it with your Reactive extension for that.

The RxGesture provides a method .tapGesture() for recognizing user interaction, but before binding to that action, we have to filter user gestures to only those which have a recognized state.

As you can see, the selectItem(index: Int) method is called inside a closure of animateClick(completion: @escaping () -> Void). This is a UIView’s extension responsible for the scale animation of our item.

The code example above presents handling user interaction for profileItem. However, do not forget to handle the rest of the items in the same way!

Putting it all together in the Tab Bar Controller

Almost everything is done, now we have to put all the parts together! The CustomTabBarController is a perfect place for that, but first, we will need a simple view controller for handling different screens associated with Tab Bar items.

The above example omits auto-layout and properties configuration intentionally as it is a simple thing to add. If you want to use mine, check the GitHub repository at the end of the article. Each view controller is initialized with CustomTabItem, thanks to that we can display on the screen item’s name.

Coming back to tab bar controller, besides setups for hierarchy and layout, we have to configure some properties.

Firstly, hide the native Tab Bar which is accessible under the class property named — tabBar.

Secondly, we will set translatesAutoresizingMaskIntoConstraints to false, and add some shadow to our customTabBar component.

Finally, we have to set the selectedIndex property of the Tab Bar Controller to the starting value (the most common case is to set the value to 0) and set View Controllers which will be handled by Tab Bar Controller. As our implementation of Tab Bar Item has a property for associated View Controller, and it is CaseIterable we can easily map all cases of our enum to the View Controller value assigned to each of them. After mapping, they are set using:

setViewControllers(_ controllers: [UIViewController], animated: true)

Now there is only one step left, we have to handle changes of currently selectedIndex.

If you paid attention before you will immediately know how we will achieve that. Of course by subscribing to the previously created Observable sequence from CustomTabBar class.

As we do not expect to get any error here we can use the bind method instead of subscribing. For each emitted index value we select a specific tab using theselectTabWith(index: Int) method, which is assigning a passed as argument index to the Tab Bar Controller’s selectedIndex property.

The final result of Custom Tab Bar implementation

And here we are! Appreciating our new beautiful custom Tab Bar!

If you are still reading, that means now you know how to create your custom Tab Bar! Congrats! Thank you so much for your time, hope you liked it.

Feel free to ask any questions in the comments section. Moreover, if you want to check the whole implementation you can find the repository here, at my GitHub page :)

--

--