# Background

In this blog, I will introduce how to use custom renderer to customize the back button icon and text in app’s navigation bar.

There is reason that why I have to log this blog. Let’s say, if you want to customize some control in Xamarin.Forms using custom render, usually you will do this: First, learn what is the native control that is wrapped in Xamarin.Forms, then you create a new class which inherits from that control’s class. After that, you will create a renderer class for that control in the platform-specific project which is Android, iOS or UWP. You will use the normal format how custom renderer does on that class and implement the OnElementChanged method for the customization logic, the OnElementChanged method was called when the control was generated. At the end, you will be able to use that new control in your XAML page file.

The official document of how to use custom renderer is here: Xamarin.Forms Custom Renderers

## Difficulties

It’s not that easy / standard to implement the custom renderer for navigation bar’s back button icon and text as we mentioned in the title. The reason for that is as below:

Let’s use Android as example. As I mentioned above, the Back button is shown on the Toolbar, and Toolbar is shown only when the Page is in a Navigation Stack. Therefore, the first thing comes to mind should be that we need to rewrite the control for NavigationPage. Based on that thought, the implementation should be as below:

In that way, we should have changed the back button’s icon and text. Actually we did, but there will be issue, I will explain later in this blog.

The usage of that control is easy, just open the App.xaml.cs file, make the MainPage a navigation page and then add a button click event as below:

## What’s the problem here?

First, in my demo/sample for implementing this, I will use this structure to demo: I have a page called MainPage which will be the first page to be loaded. Then I will have another 2 pages which called SecondPage and ThirdPage. In the MainPage and SecondPage, there will be a Label showing what page this is and a button that will do the navigation to next page. In the ThirdPage, there is only one Label showing this is the third page.

Ok, back to the problem. If we implement the custom renderer that way, you will find that the back button icon and text will show as the default effect about 1 seoncd sometimes especially on the emulators whose RAM are less or some slow real devices. After showing the default icon and text, it will show the custom icon and text as we expected. Of course this cannot be accpeted by the users.

### Root cause

If you check on the source code for that part in Xamarin.Android, if we search for OnPushAsync method, you will find the implementaion as below:

From the above, we can understand that whenever the navigation is done, Xamarin.Android will set the NavigationIcon every time. Since we are calling base.OnElementChanged(e), that’s why it will be set back to default at first then the custom renderer will take effect, which casued this issue.

# Solution

So basically what we need to do is just as Xamarin does, we are not going to change the behavior when generating the control ( which means implementing in the OnElementChanged method ), but to override the OnPushAsync method.

But here we will have another difficulty, which is the implementation between Android and iOS cannot be exactly the same. If you read further, you will find Android is easier. The reason is that we only have to check whether in current page, Android will show the navigation bar items or not, if not, we just leave it. Otherwise we will modify the toolbar items, that’s all. But iOS will be a little more complicated. Let’s see how it’s done.

## For Android

1. Create a class named CustomNavigationPage in the .NET Standard project, and this class inherits from NavigationPage to make our custom renderer applied to that class.

Code as below:

Notice that there is no implementation at all for that class. It's just a target to which we will apply the custom renderer implementation.

1. In Android project, create class named NavigationPageRenderer, override the OnPushAsync method.

Code as below:

Here is some explaination for Xamarin.Forms custom renderer.

First is a necessary attribute, which is the first line of code: [assembly: ExportRenderer(typeof(CustomNavigationPage), typeof(NavigationPageRenderer))]. This line of code means: apply the implementation of NavigationPageRenderer to CustomNavigationPage class which is the newly created class in .NET standard project.

The other key point is, why this renderer inherits from Xamarin.Forms.Platform.Android.AppCompat.NavigationPageRenderer instead of Xamarin.Forms.Platform.Android.NavigationRenderer. The reason is for compatibility for older Android versions which AppCompat is for.

The last is the logic here, quite straightforward, check whether toolbar exists, whether icon is null or not. If both exists, then set the icon using Android resource, and set the text using your own title text.

2. Usage:

InApp.xaml.csfile, set the MainPage to NavigtaionPage, code as below:

Then in the MainPage’s button click event, call the normal PushAsync method, code as below:

That’s how it’s done for Android part.

## For iOS

iOS is a little more complicated than Android, the reason is due to the below 2 difficulties.

### Difficulty 1

The Back button for iOS is just a UIButton. And when it’s needed to be shown, there is a default effect for that button implemented by Xamarin.iOS. If we are going to customize this button, we are not rewriting the control implementation but to set the TopViewController.NavigationItem.LeftBarButtonItems. When the value is null, iOS will apply the default back button, but if we set the value for that LeftBarButtonItems, iOS will show what we set for the button items.

You will notice there is also a RightBarButtonItems in the TopViewController.NavigationItem, the reason we are setting the left one is that by default page is LTR (left to right), so the back button should be on the left top corner.

Below is the difference of navigation bar between the 3 platforms in Xamarin:

So for the nav bar in iOS, we can try to understand it as this way: the left part is LeftBarButtonItems, in the middle it’s the PageTitle, and on the right it’s the RightBarButtonItems. By default, button items for both left and right are null.

So the first difficulty for us is to understand that we are not modifying but set the property to meet the requirement.

### Difficulty 2

It’s easier for Android part is that whether we modify or not modify the back button’s icon and text is determined easily. We simply check whether the navbar is null or not and also the same for navbar’s icon. If both are not null, we modify the icon and title, that’s all.

But for iOS part, we can always set the LeftBarButtonItems, but this will lead us to a problem. You will find the first loaded page, aka the MainPage will also have a back button in the nav bar which is obviously not correct. When you click on the back button on that page, definitely there can be an exception if we don’t handle it correctly.

Therefore, we have to implement the logic around Navigation Stack to figure out whether we really need to set the LeftBarButtonItems, or we use the default behavior.

To achieve the above, in our custom renderer class for iOS, not only we need to implement the OnPushAsync method, but also the OnPopViewAsync method.

### Implementation

1. Create a new class NavigationPageRendereriOS under iOS project which inhertis from Xamarin.Forms.Platform.iOS.NavigationRenderer. We need to declare 2 private fields in it, which are navigationStack and CurrentNavigationPage. Check below code structure:

2. Let’s implement 2 methods, one is SetImageTitleBackButton, which is to set the LeftBarButtonItems. The other is the opposite which is to set the default effect. This method will have 2 effects, if there is no page to navigate back, there will be no back button at all. But if for some specifc page, you do not want to use the custom renderer back button, you call also call this method to show the default one.

Check code for SetImageTitleBackButton as below:

From the above code, we can see that TopViewController.NavigationItem.LeftBarButtonItems is actually an array which can contain 2 elements. Both elements are for type UIBarButtonItem. In my code, I put the bundle image as the first element which is an down arrow, and the second element is a string which to be displayed after the image.

Let’s check the meaning for the 3 parameters for that method.

• imageBundleName: this parameter indicates the name of the bundle image, which in my case is the down arrow icon. Type of the parameter is string. About how to set a bundle image in Xamarin.iOS, check here: Displaying an image in Xamarin.iOS
• buttonTile: this is a single string used to set the text after the arrow icon. We can set it to anything we want to show, such as back, return, etc.
• horizontalOffset：as the name, it’s the horizontal offset to set the offset between bundle image and text.

Now let’s take a look at the implementation of method SetDefaultBackButton:

Quite staightforward, just set the LeftBarButtonItems to null.

3. Now let’s define the logic when to set the custom button and when to set the default. In my sample project, it’s quite simple logic: if the page is the first loaded page, which is MainPage, then we set to default back button. Otherwise, we set our custom back button.

Code as below:

Notice the first parameter “Down”, it’s the bundle image name that I set for the down arrow.

4. Now let’s implement the OnPushAsync method which is the easier one. Code as below:

Nothing much to explain here. It first calls the basic implementation of the OnPushAsync, after that, set the back button following the logic that we defined.

5. At the end, it’s the OnPopViewAsync method, code as below:

The main code here is: var returnPage = stack[stack.Count - 2];, which gives us the next returned page. And then pass this page to the SetBackButtonPage method, so that we can set the correct back button.

The reason it’s minus 2 (-2) is because array starts from 0. Assume that we navigated twice and the current page is ThirdPage, at the moment, the stack.Count returns 3. That’s because there are totally 3 elements in the navigation stack. If you click on back button, what we return to is the SecondPage, which is stack[1] obviously. So, 3-2=1, explained why we are minusing 2.

# Ending

At this moment, this demo is done. Actually we can do lots of other customizations regarding this feature. But to demonstrate, or to meet the common usage, I just set all the back buttons to a unified style. You can also check the whole sample here.

You need to set install_url to use ShareThis. Please set it in _config.yml.