Creating UI Tabs in Unity: Part 4

Posted On: 2020-08-31

By Mark

Today's post is part 4 in a series about how to add tabs to an in-game UI in Unity. In part 3 I explored adding polish via controller support. In this part, I'll cover some cosmetic polish, focusing first on animation and then branching out to some other options.

Animation Using SLayout

SLayout is a UI animation utility written by the folks at Inkle. While I haven't been using it long, I've found it quick to pick up and simple to use, so I'll be using it in this example*.

Getting started with SLayout is relatively simple: download* the source from github, and copy the "SLayout" subfolder (and license) into your project's "assets" folder**. Once included in your project, Unity should automatically include it as part of your project***. Lastly, if your project does not use Text Mesh Pro , you may need to delete the "SLayout+TMPro" file - as that one (optional) file depends on Text Mesh Pro.

Once SLayout is in your project, you should have access to the SLayout component. Add that component to each of the tab content areas, making sure that the Canvas Group and SLayout components are on the same object*.

With SLayout in place, next we need to add it to the TabPane:

public class TabPane : MonoBehaviour
{
    public Selectable FirstItemInPane;
    public CanvasGroup ContentDisplay;
    public SLayout LayoutAnimator;
}

As well as update the SetPickedTab to use the groupAlpha property of the SLayout component, instead of directly setting the canvas group's alpha value:

protected void SetTabState(int index, bool picked)
{
    TabPair affectedItem = TabCollection[index];
    affectedItem.TabContent.ContentDisplay.interactable = picked;
    affectedItem.TabContent.ContentDisplay.blocksRaycasts = picked;
    affectedItem.TabContent.LayoutAnimator.groupAlpha = picked ? 1 : 0;
    affectedItem.TabButton.image.sprite = picked ? TabIconPicked : TabIconDefault;
}

Then, use the inspector to set the SLayout property of each of the tab panes*. With that in place, you'll be all set to use SLayout to animate changes to the alpha values.

Actually animating using SLayout is quite simple: simply call the Animate method, give it a duration (in seconds), and give it an action to perform. The duration can be just about anything, so so we can add a couple fields to the TabStrip component, so that designers can set it in the inspector:

public float FadeInDuration;
public float FadeOutDuration;

And set use it in the PickTab method:

public void PickTab(int index)
{
   TabCollection[CurrentTabIndex].TabContent.LayoutAnimator.Animate(FadeOutDuration, () => 
       SetTabState(CurrentTabIndex, false)
   );
   CurrentTabIndex = index;
   TabCollection[CurrentTabIndex].TabContent.LayoutAnimator.Animate(FadeInDuration, () =>
       SetTabState(CurrentTabIndex, true)
   );
   for (var i = 0; i < TabCollection.Length; i++)
   //Omitted for brevity...

And, just like that, the tab pane will fade out the previous tab and fade in the new one. Unfortunately, however, both animations occur simultaneously, which may not be what you want. Fortunately, SLayout supports using callbacks so that you can delay code until after the animation completes:

public void PickTab(int index)
{
   TabCollection[CurrentTabIndex].TabContent.LayoutAnimator.Animate(FadeOutDuration, () => 
       SetTabState(CurrentTabIndex, false),
       ()=>{
           CurrentTabIndex = index;
           TabCollection[CurrentTabIndex].TabContent.LayoutAnimator.Animate(FadeInDuration, () =>
           SetTabState(CurrentTabIndex, true)
       );
       for (var i = 0; i < TabCollection.Length; i++)
       //Omitted for brevity...
   });

This same pattern*, can be used for most any element that you wish to animate. What's more, for anything that SLayout does not directly support, you can use the AnimateCustom method to manually perform the tween**.

Swapping Buttons

Animations aren't the only way to add polish to the tabs. The tab buttons are another possible area for improvement. While Unity's built-in buttons do work just fine for this, many may wish to use custom button controls to fit a particular user experience - whether that's a third-party component or writing their own. Since the tab strip is relatively simple in design, it should be reasonably straightforward to substitute any other component for the button: change the button type on the TabPair, and then adjust both the onClick registration and the sprite swap to use the correct functionality for whichever replacement type one is using.

Unselectable Tabs

Another option for a customized UX is to eliminate the selectabilty of the tabs altogether. While this would be problematic for most standard UIs, it might be desirable if one wants to display non-interactable tabs in a heads-up display. Fortunately, this is pretty simple to do, as CanvasGroups provide a convenient way to disable interaction: the interactable and blocksRaycasts attributes can be used to adjust how a HUD interacts with the mouse pointer. For the tab strip itself, the PickTab method is set to be public - which means it's quite possible to setup external, non-cursor methods for picking tabs (such as pressing a particular key, or automatically under certain conditions.)

Conclusion

Although there are plenty more tweaks one can make to the tab control, this is all I will write about for now. I hope you've enjoyed this (surprisingly lengthy) tutorial. If you have any questions, or would like more tutorials like this, please let me know.