xamarin / Xamarin.Forms

Xamarin.Forms is no longer supported. Migrate your apps to .NET MAUI.
https://aka.ms/xamarin-upgrade
Other
5.62k stars 1.87k forks source link

[Enhancement] Allow providing custom ShellSectionRootRenderer #7808

Closed DeanFaizal closed 4 years ago

DeanFaizal commented 5 years ago

Summary

For Shell customization, the ShellRenderer itself exposes many virtual methods which are overridable to create custom IShell..Renderers such as CreateShellSectionRenderer() https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Platform.iOS/Renderers/ShellRenderer.cs. However more granular renderers in lower levels are not exposed. For example ShellSectionRootRenderer is private to ShellSectionRenderer https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Platform.iOS/Renderers/ShellSectionRenderer.cs line 65.

API Changes

Add CreateShellSectionRootRenderer() to IShellSectionRenderer instead of instantiating the renderer in LoadPages() https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Platform.iOS/Renderers/ShellSectionRenderer.cs line 179.

Add CreateShellSectionRootHeader() to IShellSectionRootRenderer instead of instantiating it internally in UpdateHeaderVisibility() https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Platform.iOS/Renderers/ShellSectionRootRenderer.cs line 208.

Intended Use Case

To be able to modify tab styles using a custom Visual - MyVisual

[assembly: ExportRenderer(typeof(Shell), typeof(MyShellRenderer), new[] { typeof(MyVisual) })]
namespace Sample.iOS.Visual
{
    public class MyShellRenderer : ShellRenderer
    {
        protected override IShellSectionRenderer CreateShellSectionRenderer(ShellSection shellSection)
        {
            return new MyShellSectionRenderer(this);
        }
    }
}
namespace Sample.iOS.Visual
{
    public class MyShellSectionRenderer : ShellSectionRenderer
    {
        public MyShellSectionRenderer(IShellContext context) : base(context)
        {
        }

        //This is what is needed
        public override IShellSectionRootRenderer CreateShellSectionRootRenderer(...)
        {
            ... return new MyShellSectionRootHeader(...);
        }
    }
}

Currently, the whole ShellRenderer needs to be replaced just to customize tab styles.

techduggu commented 5 years ago

Hi Team, I'd like to pick this up. I'm working on it and trying to create a test case. I will raise a PR soon. Thanks!

techduggu commented 5 years ago

@DeanFaizal I've implemented the above changes and trying to create a test page for this. I referred the code example you've shared:

//This is what is needed
public override IShellSectionRootRenderer CreateShellSectionRootRenderer(...)
{
            ... return new MyShellSectionRootHeader(...);
}

If we create our own MyShellSectionRootHeader, we can't return it inside CreateShellSectionRootRenderer(...) method as it will throw the conversion error (because MyShellSectionRootHeader (inherited from ShellSectionRootHeader) doesn't implement IShellSectionRootRenderer).

However, the below code will work if we want to customize the ShellSectionRootRenderer (after I implemented the above changes):

public class MyShellSectionRenderer : ShellSectionRenderer
{
    public MyShellSectionRenderer(IShellContext context) : base(context)
    {
    }

    //This is what is needed
    protected override IShellSectionRootRenderer CreateShellSectionRootRenderer(ShellSection shellSection)
    {
        var renderer = base.CreateShellSectionRootRenderer(shellSection);
        if (renderer != null)
        {
            //customize your stuff here, for example:
            (renderer as ShellSectionRootRenderer).TabBarItem.BadgeColor = UIColor.Black;
        }
        return renderer;
    }
}

Does it fulfill your need or do you have any example (specifically for customizing ShellSectionRootHeader) in-case I'm missing anything? Thanks!