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.
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.
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