Search code examples
xamlxamarin.formsxamarin.androidxamarin.ioscustom-renderer

Xamarin forms Shell Custom icon for selected tab


I'm playing with the COOL xamarin shell, but I didn't found a way to change icon of the selected tab.

<TabBar Route="sections">
    <Tab Title="home">
        <Tab.Icon>
            <FontImageSource FontFamily="{StaticResource AppIcons}" Glyph="{x:Static framework:Icons.HomePage}" />
        </Tab.Icon>
        <ShellContent ContentTemplate="{DataTemplate home:HomePage}" Route="home" />
    </Tab>

The goal is to use Icons.HomePageFilled instead of Icons.HomePage for this tab only when it's selected. Same logic should apply to other tabs.

I think I got lost in the solutions found on the web. They talk about Custom renderers(ShellTabLayoutAppearanceTracker), Visual states, effects etc ... But I do not know if it is feasible and what is the ideal solution


Solution

  • You need to use custom renderer of shell to customize the tabbar selected icon on each platform.

    In iOS, override the CreateTabBarAppearanceTracker method:

    [assembly: ExportRenderer(typeof(AppShell), typeof(MyShellRenderer))]
    namespace App30.iOS
    {
        public class MyShellRenderer : ShellRenderer
        {
            protected override IShellSectionRenderer CreateShellSectionRenderer(ShellSection shellSection)
            {
                var renderer = base.CreateShellSectionRenderer(shellSection);
                if (renderer != null)
                {
    
                }
                return renderer;
            }
    
            protected override IShellTabBarAppearanceTracker CreateTabBarAppearanceTracker()
            {
                return new CustomTabbarAppearance();
            }
        }
    
        public class CustomTabbarAppearance : IShellTabBarAppearanceTracker
        {
            public void Dispose()
            {
    
            }
    
            public void ResetAppearance(UITabBarController controller)
            {
    
            }
    
            public void SetAppearance(UITabBarController controller, ShellAppearance appearance)
            {
                UITabBar myTabBar = controller.TabBar;
    
                if (myTabBar.Items != null)
                {
                    UITabBarItem itemOne = myTabBar.Items[0];
    
                    itemOne.Image = UIImage.FromBundle("tab_about.png");
                    itemOne.SelectedImage = UIImage.FromBundle("tab_feed.png");
    
    
                    UITabBarItem itemTwo = myTabBar.Items[1];
    
                    itemTwo.Image = UIImage.FromBundle("tab_feed.png");
                    itemTwo.SelectedImage = UIImage.FromBundle("tab_about.png");
    
                    //The same logic if you have itemThree, itemFour....
                }
    
            }
    
            public void UpdateLayout(UITabBarController controller)
            {
    
            }
        }
    }
    

    In Android, override the CreateBottomNavViewAppearanceTracker method:

    [assembly: ExportRenderer(typeof(AppShell), typeof(MyShellRenderer))]
    namespace App30.Droid
    {
        public class MyShellRenderer : ShellRenderer
        {
            public MyShellRenderer(Context context) : base(context)
            {
            }
    
            protected override IShellBottomNavViewAppearanceTracker CreateBottomNavViewAppearanceTracker(ShellItem shellItem)
            {
                return new CustomBottomNavAppearance();
            }
        }
    
        public class CustomBottomNavAppearance : IShellBottomNavViewAppearanceTracker
        {
            public void Dispose()
            {
    
            }
    
            public void ResetAppearance(BottomNavigationView bottomView)
            {
    
            }
    
            public void SetAppearance(BottomNavigationView bottomView, ShellAppearance appearance)
            {
                IMenu myMenu = bottomView.Menu;
    
                IMenuItem myItemOne = myMenu.GetItem(0);
    
                if (myItemOne.IsChecked)
                {
                    myItemOne.SetIcon(Resource.Drawable.tab_about);
                }
                else
                {
                    myItemOne.SetIcon(Resource.Drawable.tab_feed);
                }
    
                //The same logic if you have myItemTwo, myItemThree....
    
            }
        }
    }
    

    I uploaded a sample project here and you can check it.