Search code examples
xamarinxamarin.androidback-buttoncustom-renderer

Custom NavigationRenderer for back button not called while navigating back in xamarin forms application


Here is the code for the custom renderer i used to assign a custom icon as my back button.

namespace MyProjectName.Droid.Renderers
{
  public class MyNavigationRenderer: PageRenderer
  {
    protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Page> e)
    {
      base.OnElementChanged(e);

      var context = (Activity)Xamarin.Forms.Forms.Context;
      
      var toolbar = context.FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Droid.Resource.Id.toolbar);

      toolbar.NavigationIcon = AndroidX.Core.Content.ContextCompat.GetDrawable(context, Resource.Drawable.bbutton_nav);

    }   
  }
}

This code successfully replaces the native back arrow icon with my custom bbutton_nav. When i navigate forward(Navigate.PushAsync()), the custom icon appears on all the upcoming screens. But when i click on the back icon to go back one page(Navigate.PopAsync()), the old native back arrow reappears instead of the new custom icon that was set by the renderer. When i tried debugging , i found out that the renderer class was not getting called when navigating back(Navigation.PopAsync()). Any help on how to mitigate this issue is appreciated. Thanks


Solution

  • Create a custom renderer for NavigationPage instead of Page , and override the OnLayout method .

    Android will change the detault icon back in UpdateToolbar method , and OnLayout method is triggered every time while current page is changed.

    Android Solution

    [assembly: ExportRenderer(typeof(NavigationPage), typeof(MyNavigationRenderer))]
    namespace FormsApp.Droid
    {
        public class MyNavigationRenderer : NavigationPageRenderer
        {
            Context _context;
    
            AndroidX.AppCompat.Widget.Toolbar _toolbar;
    
            public MyNavigationRenderer(Context context) : base(context)
            {
                _context = context;
            }
    
            public override void OnViewAdded(Android.Views.View child)
            {
                base.OnViewAdded(child);
                if (child.GetType() == typeof(AndroidX.AppCompat.Widget.Toolbar))
                {
                    _toolbar = (AndroidX.AppCompat.Widget.Toolbar)child;
                    _toolbar.SetNavigationIcon(Resource.Drawable.bbutton_nav);
                }
            }
    
            protected override void OnLayout(bool changed, int l, int t, int r, int b)
            {
                base.OnLayout(changed, l, t, r, b);
    
                if (_toolbar != null)
                {
                    if (_toolbar.NavigationIcon != null)
                    {
                        _toolbar.NavigationIcon = AndroidX.Core.Content.ContextCompat.GetDrawable(_context, Resource.Drawable.bbutton_nav);
                    }
                }
            }
        }
    }
    

    Refer to https://forums.xamarin.com/discussion/183344/how-to-change-navigation-back-button-icon .

    iOS Solution

    [assembly: ExportRenderer(typeof(NavigationPage), typeof(MyRenderer))]
    namespace FormsApp.iOS
    {
        class MyRenderer : NavigationRenderer
        {
            public override void ViewDidLayoutSubviews()
            {
                base.ViewDidLayoutSubviews();
    
              
                if (this.NavigationBar.TopItem.BackBarButtonItem == null)
                {
                    this.NavigationBar.BackIndicatorImage = UIImage.FromFile("dots.png");
                    this.NavigationBar.BackIndicatorTransitionMaskImage = UIImage.FromFile("dots.png");
                    this.NavigationBar.TopItem.BackBarButtonItem = new UIBarButtonItem("", UIBarButtonItemStyle.Plain, null);
                }
            }
    
        }
    }