How to change the navigation page back icon?

In Xamarin Forms, there is no way to change the back icon in PCL. Actually, there is a property in NavigationPage class, called TitleIcon and also a function called SetTitleIcon. But they just add a Icon below the title like this.

resource

But that is not what we want, if we want like this, we need create PageRenderer for iOS and Android.

In iOS, we need to add a UIBarButtonItem into LeftBarButtonItem. But the interesting thing is, we cannot access the NavigationController.TopViewController.NavigationItem in OnElementChanged function, which we usually do the renderer things. After some research I find that we can do this in ViewWillAppear function.

using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using UIKit;
[assembly: ExportRenderer (typeof(ContentPage), typeof(ChangeBackIcon.iOS.NavigationPageRendereriOS))]
namespace ChangeBackIcon.iOS
{
public class NavigationPageRendereriOS : PageRenderer
{
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
// If you want to hide the back button in some pages,
// you can pass a value to renderer and do this.
var page = this.Element as ICanHideBackButton;
if (page != null) {
if (page.HideBackButton) {
this.NavigationController.TopViewController.NavigationItem.SetHidesBackButton (true, false);
return;
}
}
// Change back icon.
this.NavigationController.TopViewController.NavigationItem.LeftBarButtonItem =
new UIBarButtonItem (
UIImage.FromFile ("Back.png"),
UIBarButtonItemStyle.Plain,
(sender, args) => {
// This will overwrite PopView behavior in Xamarin Forms.
NavigationController.PopViewController (true);
});
}
}
}

In Android, I used the Android 6.0 SDK to build the App. The navigation bar in Xamarin.Android is the ActionBar, so we need few things to do here. First, we need to hide the App icon, then we need to replace the Up caret with custom icon, last we need to make title alignment in center. The default alignment is left, so we need to set customer view to ActionBar.

Here is the codes.

using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Android.Graphics.Drawables;
using Android.Widget;
[assembly: ExportRenderer (typeof(ContentPage), typeof(ChangeBackIcon.Droid.NavigationPageRendererDroid))]
namespace ChangeBackIcon.Droid
{
public class NavigationPageRendererDroid : PageRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Page> e)
{
base.OnElementChanged (e);
var actionBar = ((Android.App.Activity)Context).ActionBar;
// As the Title is not alignment as center, so that we need to load customer view.
actionBar.SetCustomView (Resource.Layout.CustomNavigationBarLayout);
actionBar.SetDisplayShowCustomEnabled (true);
// This function is used for hide the App Icon
actionBar.SetIcon (new ColorDrawable (Color.Transparent.ToAndroid ()));
Element.Appearing+= (object sender, EventArgs ex) => {
// Update Title and Back Icon when user go back to this page
UpdatePageTitle ();
UpdateBackIcon ();
};
}
void UpdateBackIcon()
{
var actionBar = ((Android.App.Activity)Context).ActionBar;
if (actionBar == null || actionBar.CustomView == null)
return;
// If you want to hide the back button in some pages,
// you can pass a value to renderer and do this.
var pagemodel = this.Element as ICanHideBackButton;
if (pagemodel != null) {
if (pagemodel.HideBackButton) {
// I didn't find out another way to hide the UpInicator
// I have tried this, but not work
// actionBar.SetDisplayHomeAsUpEnabled(false);
// But This is work
actionBar.SetHomeAsUpIndicator (new ColorDrawable (Color.Transparent.ToAndroid ()));
}
} else {
actionBar.SetHomeAsUpIndicator (Resource.Drawable.Back);
}
}
void UpdatePageTitle ()
{
var actionBar = ((Android.App.Activity)Context).ActionBar;
if (actionBar == null || actionBar.CustomView == null)
return;
var view = actionBar.CustomView.FindViewById<TextView> (Resource.Id.TitleText);
if (view != null)
view.Text = Element.Title;
}
protected override void OnElementPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged (sender, e);
// As we used customer view, so Title cannot be update after page loaded.
if (e.PropertyName == "Title")
UpdatePageTitle ();
}
}
}

Also, I have added some logic for hiding the back icon in some pages. As sometimes we navigation to some pages, like after login page, we don’t want to customer to go back. It works well in iOS, but not Android. In Android, I can hide the back icon in screen, but if user press the physical button, this page still can go back. The better solution for this, is designing a good navigation stack, like after use login, we can clear the navigation stack or put this page as the root of navigation stack.

If you want to the whole dome solution, please check here

https://github.com/jessejiang0214/ChangeBackIconInXF

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s