Is it possible to dynamically change the icon of a ToolbarItem?

Viewed 1296

I would like to change several icons located in the ToolbarItem of a TabbedPage, I've looked at docs here, and I'm either missing the point or maybe what I wish to achieve is not doable?

I'm currently using FontAwesomeIcons to populate icons across my app, and from a static point of view they work great. But in certain scenarios I may wish to change either the icon, or its colour or the icon pack (Light to Solid for example).

App.xaml - I use this to reference my .otf files

<Application.Resources>
    <OnPlatform x:Key="FontAwesomeProLight" x:TypeArguments="x:String">
        <On Platform="iOS" Value="FontAwesome5Pro-Light" />
    </OnPlatform>
    <OnPlatform x:Key="FontAwesomeProSolid" x:TypeArguments="x:String">
        <On Platform="iOS" Value="FontAwesome5Pro-Solid" />
    </OnPlatform>
</Application.Resources>

ExamplePage.xaml - This would be a page where I would show my icons currently (not dynamically)

<TabbedPage.ToolbarItems>
    <ToolbarItem Clicked="OnFilterOrders">
        <ToolbarItem.IconImageSource>
            <FontImageSource FontFamily="{StaticResource FontAwesomeProLight}" Glyph="{x:Static fonts:FontAwesomeIcons.Filter}" />
        </ToolbarItem.IconImageSource>
    </ToolbarItem>
</TabbedPage.ToolbarItems>

So the code at this point works perfectly to show a static icon, below is my attempt which is failing - but not throwing an error, I just get a ? instead

ExamplePage.xaml

<TabbedPage.ToolbarItems>
    <ToolbarItem Clicked="OnFilterOrders">
        <ToolbarItem.IconImageSource>
            <FontImageSource FontFamily="{DynamicResource FontAwesomeIconPack}" Glyph="{x:Static fonts:FontAwesomeIcons.Filter}" />
        </ToolbarItem.IconImageSource>
    </ToolbarItem>
</TabbedPage.ToolbarItems>

Using the code-behind of that page, I also have the following in the constructor.

Resources["FontAwesomeIconPack"] = App.Current.Resources["FontAwesomeProLight"];

Am I correct in assuming that Resources["FontAwesomeIconPack"] is linked with the pages resource dictionary, and App.Current.Resources["FontAwesomeProLight"] is linked to the app.xaml page?

I was hoping in this example I would get my existing icon to show, but it doesn't. My expectations is the same icon as before (before I change the pack) but instead I just get a ?).

2 Answers

If you want to change the style of tabded icon in run time(such as icon,fontSize) .You should use Custom Renderer .

in iOS

using System;

using UIKit;

using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

using xxx;
using xxx.iOS;

[assembly:ExportRenderer(typeof(TabbedPage),typeof(MyTabbedPageRenderer))]
namespace xxx.iOS
{
    public class MyTabbedPageRenderer:TabbedRenderer
    {
        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);
        }

        public override void ViewDidAppear(bool animated)
        {
            base.ViewDidAppear(animated);

            MessagingCenter.Subscribe<Object, int>(this, "ChangeStyle", (args, position) => {

                UpdateItem(TabBar.Items[position], "xxx.png","xxx2.png");

            });

        }

        void UpdateItem(UITabBarItem item, string icon,string selectIcon)
         {
            if (item == null)
            {
                return;
            }
                                      
            // set default icon
            if (item?.Image?.AccessibilityIdentifier == icon)
              return;
            item.Image = UIImage.FromBundle(icon);
            item.Image.AccessibilityIdentifier = icon;

            //set select icon
            if (item?.SelectedImage?.AccessibilityIdentifier == selectIcon)
                return;
            item.SelectedImage = UIImage.FromBundle(selectIcon);
            item.SelectedImage.AccessibilityIdentifier = selectIcon;


            //set font
            item.SetTitleTextAttributes(new UITextAttributes() { Font=UIFont.SystemFontOfSize(10,UIFontWeight.Light)},UIControlState.Normal);
            item.SetTitleTextAttributes(new UITextAttributes() { Font = UIFont.SystemFontOfSize(10, UIFontWeight.Light) }, UIControlState.Selected);
        }
    }
}

in Android

You can check this blog , it provide the solution in Android by using Custom Renderer .

in Forms

Use MessagingCenter to send the message when you want (such as click a button)

MessagingCenter.Send<Object, int>(this, "ChangeStyle", 0);

Things may have changed but I am merely binding the IconImageSource of the child NavigationPage to a viewmodel string and dynamic icon selection is working fine.

This is tested on Android and iOS.

Related