When dealing with the navigation bar in Xamarin Forms, is pretty limited what we can do related to customization. So when this is needed there are generally two approaches people follow:
- Hide the Xamarin Forms navigation bar and implementing a custom one using any forms layout. The problem with this approach might be handling the glitches caused by navigation transitions especially on Android when using the forms navigation bar on some pages and in other's not.
- Implement customization on each platform by using custom renderers.
In this article, we are going to focus on the second one.
The first thing to do is to create a CustomNavigationPage class that extends from the NavigationPage class and add some attached properties to achieve extra customization.
- TitlePositionProperty - This property will get/set the position for our title (Start, Center or End).
- TitleBackgroundProperty - Gets/Sets the background for the title view.
- TitleFontProperty - Gets/Sets the font for the title text.
- SubtitleFontProperty - Gets/Sets the font for the subtitle text.
- TitlePaddingProperty - Gets/Sets the padding for the title view.
- TitleMarginProperty - Gets/Sets the margin for the title view.
- TitleBorderCornerRadiusProperty - Gets/Sets the border radius for the title view.
- TitleBorderColorProperty - Gets/Sets the border color for the title view.
- TitleFillColorProperty - Gets/Sets the fill color for the title view.
- TitleBorderWidthProperty - Gets/Sets the border width for the title view.
- BarBackgroundProperty - Gets/Sets the background for the navigation bar.
- GradientColorsProperty - Gets/Sets the gradient colors background for the navigation bar.
- GradientDirectionProperty - Gets/Sets the direction for the bar background gradient.
- HasShadowProperty - Gets/Sets the shadow for the bottom of the bar.
- BarBackgroundOpacityProperty - Gets/Sets the opacity for the bar.
Also, let's create a CustomPage to support some flexibility by having a subtitle and formatted text support. To provide this let's add the following properties:
- Subtitle - Property to set a plain subtitle text
- FormattedTitle - Will provide us the flexibility to format individually every part of our title text.
- FormattedSubtitle - Will provide us the flexibility to format individually every part of our subtitle text.
For FormattedTitle & FormattedSubtitle will use type FormattedString (This will allow us to use spans to create our formatted text string).
On Android we got the AppCompat Toolbar to apply our customization. We should be able to do this by implementing a custom NavigationPageRenderer.
NOTE: Make sure to add [assembly: ExportRenderer(typeof(CustomNavigationPage), typeof(CustomNavigationPageRenderer))] to your custom renderer.
The next step will be to create a new class called CustomNavigationPageRenderer and extends from NavigationPageRenderer. After that, we are going to overwrite the method OnViewAdded to detect when Toolbar is added.
We created a FrameLayout with a LinearLayout inside that contains a title and subtitle TextView.The linear layout represents the title view as a whole (title and subtitle).
We subscribe to ChildViewAdded toolbar event to listen when Xamarin Forms adds default toolbar child text view so we can hide it and show just ours with the desired customization for the current page. Also to the page PropertyChanged event to listen to attached property changes.
By overriding SetupPageTransition we can set up the correct customization when a page is popped or pushed. When pushed we first stop listening to the previous page PropertyChanged event and start listening to the new pushed page PropertyChanged event. Finally, apply the current proper customization for the navigated page. When popped we stop listening to the current page PropertyChanged event, listen to the previous page PropertyChanged event and restore previous page customization.
Here how we achieve our customization based on the attached properties:
For positioning our title layout we set the gravity of all our views to the specified value set on CustomNavigationPage.GetTitlePosition for the current page shown.
We set the title layout name by looking for the resource identifier for the name specified in CustomNavigationPage.GetTitleBackground for the current page.
Title & Subtitle Font
Xamarin Forms Font has an Android extension ToTypeface() that gives you the native typeface equivalent to the font we set on Forms.
For setting formatted string text, Forms has a pretty handy extension method ToAttributed() that returns the formatted text equivalent for Android native platform.
Title Padding and Margin
We set the padding to our title view layout and apply the margin to the layout parameters of this layout.
Title Fill & Border Color, Corner Radius and Width
We use a method called CreateShape to create a GradientDrawable that allow us to set the title fill, border color, radius and width by setting this drawable on the title view layout background.
For the toolbar background, we set the background to the value CustomNavigationPage.GetBarBackground which is the name of the drawable to set as background. The opacity (0-1) is set by using CustomNavigationPage.GetBarBackgroundOpacity which is a value from 0 to 1, so we multiply by 255 to set the alpha of the toobar background. In the case of the bar background gradient we use a gradient drawable to set the gradient colors and direction based on CustomNavigationPage.GetGradientColors and CustomNavigationPage.GetGradientDirection values.
To set the shadow we find the FrameLayout inside the activity Window DecorView to set a GradientDrawable with the shadow colors. We set as well the right toolbar elevation to achieve the shadow effect
On iOS we got the UINavigationBar we can apply our customization to specific pages by using a custom PageRenderer. The reason we didn't use a NavigationPageRenderer as on Android is because of the lifecycle of iOS view controllers, we need to do the customization after view controller is loaded and will appear on the screen. Doing it with a navigation page renderer might be too late or too early since we don't have feedback on the view controller events, so we won't have the behavior we expect.
NOTE: Make sure to add [assembly: ExportRenderer(typeof(ContentPage), typeof(CustomPageRenderer))] to your Custom Renderer.
As you can see here we are going to apply the renderer to all the pages in the application, if you want to apply it to just one page make sure to create a new ContentPage class in your Forms Project and use that type in the ExportRenderer typeof.
The next step will be to create a new class called CustomPageRenderer and extends from PageRenderer. After that, we are going to override method ViewDidLoad to create our custom navigation bar view there and also start listening to PropertyChanged events on this page so that we can adapt customization when any property changes. Which will be a container UIView with another UIView to handle margins inside and then our title UIView which contains two UILabels one for the title and other for the subtitle.
Then we override ViewWillAppear to set our navigation bar customization, also we need to consider doing this if device is rotated or layout changes occur a good ViewDidLayoutSubviews. We set the title position as well on each on these methods.
SetupNavBar is the method that does the customization magic on iOS, since is the responsible to configure all customization that was set in CustomNavigationPage attached properties and also sets our custom title view on ParentViewController.NavigationItem.TitleView.
Title Position, Padding and Margin
To set the title position, padding and margin we basically calculate the correct Frame for the parameters set considering the position, padding, margin and text size as well.
Title & Subtitle Font
Xamarin Forms Font has an iOS extension ToUIFont() that gives you the native font equivalent to the one we set on Forms. For setting formatted string text, Forms has a pretty handy extension method ToAttributed() available on a FormattedString type that returns the formatted text equivalent for iOS native platform.
Bar Background, Opacity, Gradient Colors and Direction
We set the opacity by setting the NavigationController.NavigationBar.Alpha to our CustomNavigationPage.GetBarBackgroundOpacity. Also, we if CustomNavigationPage.GetBarBackground is set we load the UImage with this name to set it as the NavigationBar background.
We create the gradient by using CreateGradientBackground method that basically creates an UIImage based on a CAGradientLayer which properties are set based on the values of CustomNavigationPage.GetGradientColors and CustomNavigationPage.GetGradientDirection attached properties. Then set this created image as the NavigationBar background.
We set the shadow if CustomNavigationPage.GetHasShadow is true by setting NavigationController.NavigationBar layer properties: ShadowColor, ShadowOffset, ShadowOpacity.
Here some examples of how to use on Page:
//Sets the title position to end CustomNavigationPage.SetTitlePosition(this, CustomNavigationPage.TitleAlignment.End); //Sets shadow for bar bottom CustomNavigationPage.SetHasShadow(this,true); //Gets if has shadow or not bool hasShadow = CustomNavigationPage.GetHasShadow(this); //Sets the title text font to Micro CustomNavigationPage.SetTitleFont(this, Font.SystemFontOfSize(NamedSize.Micro));
If we want to have a subtitle just make sure your page inherits from CustomPage.
You can check the full source code here: