Answered by:
Cross Platform gradient navigationbar

Question
-
User157298 posted
Hi all.
I am trying to make a NavigationBar containing a gradient with top to bottom direction for ios and android.
I took a look on some custom renderer, but i found an issue on IOS because the gradient doesn`t fill full height of frame view, making a line on the middle of the frame, like the image attached.
I have already tried this links to solve this issue but no success until now:
https://github.com/CrossGeeks/CustomNavigationBarSample
https://www.xamboy.com/2017/12/06/navigation-bar-customization-in-xamarin-forms/
https://github.com/vackup/StatusBarGradientBackground
https://blog.wilsonvargas.com/personalizando-un-navigationbar/
Some coding about my try:
Cross project NavigationPageGradientHeader.cs
public class NavigationPageGradientHeader : NavigationPage { public NavigationPageGradientHeader(Page root) : base(root) { }
public static readonly BindableProperty BottomColorProperty = BindableProperty.Create(propertyName: nameof(BottomColor), returnType: typeof(Color), declaringType: typeof(NavigationPageGradientHeader), defaultValue: Color.Red); public static readonly BindableProperty TopColorProperty = BindableProperty.Create(propertyName: nameof(TopColor), returnType: typeof(Color), declaringType: typeof(NavigationPageGradientHeader), defaultValue: Color.Black); public Color BottomColor { get { return (Color)GetValue(BottomColorProperty); } set { SetValue(BottomColorProperty, value); } } public Color TopColor { get { return (Color)GetValue(TopColorProperty); } set { SetValue(TopColorProperty, value); } } }
Cross project App.cs public App() { InitializeComponent();
MainPage = new GradientNavigationHeader.Controls.NavigationPageGradientHeader(new MainPage()) { TopColor = Color.Green, BottomColor = Color.Yellow }; //MainPage = new NavigationPage(new MainPage()); }
IOS Custom Renderer NavigationPageGradientHeaderRenderer.cs
[assembly: ExportRenderer(typeof(NavigationPageGradientHeader), typeof(NavigationPageGradientHeaderRenderer))] namespace GradientNavigationHeader.iOS { public class NavigationPageGradientHeaderRenderer : NavigationRenderer { public override void ViewWillAppear(bool animated) { base.ViewWillAppear(animated);
var control = (NavigationPageGradientHeader)this.Element; var gradientLayer = new CAGradientLayer(); gradientLayer.Bounds = NavigationBar.Bounds; gradientLayer.Colors = new CGColor[] { control.BottomColor.ToCGColor(), control.TopColor.ToCGColor() }; gradientLayer.StartPoint = new CGPoint(1.0, 0.0); gradientLayer.EndPoint = new CGPoint(0.0, 1.0); UIGraphics.BeginImageContext(View.Frame.Size); gradientLayer.RenderInContext(UIGraphics.GetCurrentContext()); UIImage image = UIGraphics.GetImageFromCurrentImageContext(); UIGraphics.EndImageContext(); NavigationBar.SetBackgroundImage(image, UIBarMetrics.Default); } }
}
Is there any idea on how to make the gradient fill the navigationbar frame?
Wednesday, August 1, 2018 2:10 AM
Answers
-
User369979 posted
@RafaelAddesso Since you just set your Layer's frame to the navigation bar's bounds, actually the navigation bar's total includes the navigation bar's height and the status bar's height. Try to add the status bar's height in your renderer to avoid this white space:
public override void ViewWillAppear(bool animated) { base.ViewWillAppear(animated); var statusFrame = UIApplication.SharedApplication.StatusBarFrame; var navigationFrame = NavigationBar.Frame; var gradientLayer = new CAGradientLayer(); gradientLayer.Frame = new CGRect(0, 0, navigationFrame.Width, statusFrame.Height + navigationFrame.Height); gradientLayer.Colors = new CGColor[] { control.BottomColor.ToCGColor(), control.TopColor.ToCGColor() }; gradientLayer.StartPoint = new CGPoint(1.0, 0.0); gradientLayer.EndPoint = new CGPoint(0.0, 1.0); UIGraphics.BeginImageContext(gradientLayer.Frame.Size); gradientLayer.RenderInContext(UIGraphics.GetCurrentContext()); UIImage image = UIGraphics.GetImageFromCurrentImageContext(); UIGraphics.EndImageContext(); NavigationBar.SetBackgroundImage(image, UIBarMetrics.Default); }
- Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
Friday, August 3, 2018 6:19 AM
All replies
-
User329247 posted
You can create custom renderer of stack layout to solve your issue and create custom navigation bar using StackLayout:
CustomStackLayout.cs:
using Xamarin.Forms; namespace XYZ.CustomControl { //gradient background public class CustomStackLayout:StackLayout { /// <summary> /// Used for gradient on dashboard screen. /// </summary> public Color StartColor = Color.FromHex("#F37021"); public Color EndColor = Color.FromHex("#F94437"); } }
In Droid->Resources->Drawable: GradientStackLayout.xml
<?xml version="1.0" encoding="utf-8" ?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <stroke android:width="1dp" android:color="#00ffffff" /> <solid android:color="#ffffff" /> <corners android:radius="0dp" /> <gradient android:angle="-90" android:type="linear" android:startColor="#F37021" android:endColor="#F94437"/> </shape>
Droid:
using Xamarin.Forms.Platform.Android; using Xamarin.Forms; using XYZ.CustomControl; [assembly: ExportRenderer(typeof(CustomStackLayout), typeof(XYZ.Droid.Renderer.CustomStackLayout))] namespace XYZ.Droid.Renderer { public class CustomStackLayout: VisualElementRenderer<StackLayout> { protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.StackLayout> e) { base.OnElementChanged(e); //set gradient background to stackLayout SetBackgroundDrawable(Resources.GetDrawable(Resource.Drawable.GradientStackLayout)); } } }
iOS:
using System; using System.Collections.Generic; using System.Text; using UIKit; using Xamarin.Forms; using Xamarin.Forms.Platform.iOS; using XYZ.CustomControl; using XYZ.iOS.Renderer; using CoreGraphics; using CoreAnimation; using System.ComponentModel; [assembly: ExportRenderer(typeof(CustomStackLayout), typeof(CustomStackLayoutiOS))] namespace XYZ.iOS.Renderer { public class CustomStackLayoutiOS:VisualElementRenderer<StackLayout> { //Apply gradient background to stackLayout public override void Draw(CGRect rect) { base.Draw(rect); CAGradientLayer layer = new CAGradientLayer(); layer.Frame = rect; layer.Colors = new CGColor[] { ((CustomStackLayout)Element).StartColor.ToCGColor(), ((CustomStackLayout)Element).EndColor.ToCGColor() }; if (Layer.Sublayers[0] is CAGradientLayer) Layer.ReplaceSublayer(Layer.Sublayers[0], layer); else Layer.InsertSublayer(layer, 0); } protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); if (e.PropertyName == "Width") SetNeedsDisplay(); } } }
And by using this custom stack layout create your custom gradient navigation bar. Hope this may solve your issue.
Wednesday, August 1, 2018 10:56 AM -
User157298 posted
@M_Shah04 How do i add the stacklayout to navigationbar?
Friday, August 3, 2018 1:18 AM -
User329247 posted
@RafaelAddesso You can't add StackLayout to navigation bar that's y I have already mentioned that you need to create custom navigationbar, like set navigation bar visibility to false and add this in your xaml page to design.
Friday, August 3, 2018 4:30 AM -
User369979 posted
@RafaelAddesso Since you just set your Layer's frame to the navigation bar's bounds, actually the navigation bar's total includes the navigation bar's height and the status bar's height. Try to add the status bar's height in your renderer to avoid this white space:
public override void ViewWillAppear(bool animated) { base.ViewWillAppear(animated); var statusFrame = UIApplication.SharedApplication.StatusBarFrame; var navigationFrame = NavigationBar.Frame; var gradientLayer = new CAGradientLayer(); gradientLayer.Frame = new CGRect(0, 0, navigationFrame.Width, statusFrame.Height + navigationFrame.Height); gradientLayer.Colors = new CGColor[] { control.BottomColor.ToCGColor(), control.TopColor.ToCGColor() }; gradientLayer.StartPoint = new CGPoint(1.0, 0.0); gradientLayer.EndPoint = new CGPoint(0.0, 1.0); UIGraphics.BeginImageContext(gradientLayer.Frame.Size); gradientLayer.RenderInContext(UIGraphics.GetCurrentContext()); UIImage image = UIGraphics.GetImageFromCurrentImageContext(); UIGraphics.EndImageContext(); NavigationBar.SetBackgroundImage(image, UIBarMetrics.Default); }
- Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
Friday, August 3, 2018 6:19 AM