#Xamarin Forms #Shell: Creating a Gradient Flyout

David Ortinau’s Xappy was a fun demo of some of the features of the Rendering capabilities of the new Xamarin Forms Shell. That has served as an inspiration for the gradient flyout in this article. We’ve extended it to handle some Android Dispose issues in Shell that in the next Xamarin Forms should go away.

Here’s how it looks when running:

2019-07-26 12_47_38-iPhone 7 iOS 12.4.png

In Shell, you can enhance the UI with simple Renderers you add as classes to your iOS and Android projects. You decorate them with [ExportRender] to tell Xamarin to use them.

Style

Add this to the App.xaml so that you have the Flyout styles ready to be accessed. Note that they are dynamic so that they can be updated live.


    <Color x:Key="FlyoutGradientStart">#347FB9</Color>
    <Color x:Key="FlyoutGradientEnd">#38AECC</Color>

Renderers

The easy one in this example is iOS, it’s straight forward, just add it anywhere in your iOS project:

using System;
using CoreAnimation;
using CoreGraphics;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
 
 
[assemblyExportRenderer(typeof(Shell), typeof(App.iOS.Renderers.GradientShellRenderer))]
namespace App.iOS.Renderers
{
	/// <summary>
	/// Inspired by David Ortinau's Xappy renderer
	/// </summary>
    public class GradientShellRenderer : ShellRenderer
    {
        private CAGradientLayer _flyoutBackground = null;
 
        protected override void OnElementSet(Shell element)
        {
            base.OnElementSet(element);
        }
 
        protected override IShellSectionRenderer CreateShellSectionRenderer(ShellSection shellSection)
        {
            var renderer = base.CreateShellSectionRenderer(shellSection);
            if (renderer == nullreturn null;
 
            if (renderer is ShellSectionRenderer r)
            {
	            r.NavigationBar.ShadowImage = new UIImage();
            }
 
            return (IShellSectionRenderer)renderer;
        }
 
        protected override IShellFlyoutContentRenderer CreateShellFlyoutContentRenderer()
        {
 
            var flyout = base.CreateShellFlyoutContentRenderer();
            flyout.WillAppear += OnFlyoutWillAppear;
 
            var tv = (UITableView)flyout.ViewController.View.Subviews[0];
            tv.ScrollEnabled = false;
 
            return flyout;
        }
 
        /// <summary>
        /// Only grab the bounds of the View when View rendering calculations are already done
        /// </summary>
        private void OnFlyoutWillAppear(object senderEventArgs e)
        {
            if (_flyoutBackground == null && sender != null && sender is IShellFlyoutContentRenderer flyout)
			{
	            var view = flyout.ViewController.View;
 
                _flyoutBackground = new CAGradientLayer
                {
	                Frame = new CGRect(0, 0, view.Bounds.Widthview.Bounds.Height),
	                Colors = new []
	                {
		                ((ColorApp.Current.Resources["FlyoutGradientStart"]).ToCGColor(), ((ColorApp.Current.Resources["FlyoutGradientEnd"]).ToCGColor()
	                }
                };
 
                flyout.ViewController.View.Layer.InsertSublayer(_flyoutBackground, 0);
                flyout.WillAppear -= OnFlyoutWillAppear;
            }
        }
    }
}

Next in your Android project add this one. Note that we handle the Dispose due to a bug in Xamarin Shell in 4.1

The dispose needs to be taken care of or your app will crash a lot because of

ShellSectionRenderer.UnhookEvents ()

System.NullReferenceException: Object reference not set to an instance of an object
[assemblyExportRenderer(typeof(Shell), typeof(App.Droid.Renderers.GradientShellRenderer))]
 
namespace App.Droid.Renderers
{
	/// <summary>
	/// Inspired by David Ortinau's Xappy renderer
	/// </summary>
    public class GradientShellRenderer : ShellRenderer
    {
	    bool _disposed;
        public GradientShellRenderer(Context context) : base(context)
        {
        }
 
        protected override void OnElementSet(Shell element)
        {
            base.OnElementSet(element);
        }
 
        protected override IShellSectionRenderer CreateShellSectionRenderer(ShellSection shellSection)
        {
            var renderer = base.CreateShellSectionRenderer(shellSection);
            return (IShellSectionRendererrenderer;
        }
 
        protected override IShellFlyoutRenderer CreateShellFlyoutRenderer()
        {
            var flyout = base.CreateShellFlyoutRenderer();
 
            return flyout;
        }
 
        protected override IShellFlyoutContentRenderer CreateShellFlyoutContentRenderer()
        {
 
            var flyout = base.CreateShellFlyoutContentRenderer();
 
            try
            {
	            GradientDrawable gradient = new GradientDrawable(
		            GradientDrawable.Orientation.BottomTop,
		            new Int32[]
		            {
			            ((ColorApp.Current.Resources["FlyoutGradientStart"]).ToAndroid(),
			            ((ColorApp.Current.Resources["FlyoutGradientEnd"]).ToAndroid()
 
		            }
	            );
 
	            var cl = ((CoordinatorLayoutflyout.AndroidView);
	            cl.SetBackground(gradient);
 
	            var g = (AppBarLayoutcl.GetChildAt(0);
	            g.SetBackgroundColor(Color.Transparent.ToAndroid());
	            g.OutlineProvider = null;
 
	            var header = g.GetChildAt(0);
	            header.SetBackgroundColor(Color.Transparent.ToAndroid());
 
            }
            catch (Exception e)
            {
	            Logger.LogError(e);
            }
 
            return flyout;
        }
 
        protected override void Dispose(bool disposing)
        {
	        if (_disposed)
	        {
		        return;
	        }
 
	        if (disposing && Element!=null)
	        {
		        Element.PropertyChanged -= OnElementPropertyChanged;
		        Element.SizeChanged -=
			        (EventHandlerDelegate.CreateDelegate(typeof(EventHandler), this"OnElementSizeChanged"); // OnElementSizeChanged is private, so use reflection
	        }
 
	        _disposed = true;
        }
    }
}

 

Advertisements

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