dkolarevic
dkolarevic

Reputation: 11

Xamarin iOS - custom TabBar (Shell)

In my Xamarin.Forms application I need to add a bottom bar that will be visible over the tab bar. Since my app is a Shell application, I'm trying to create a custom ShellItemRenderer.

I extended the TabBar so I that could add custom view:

<local:CustomTabBar IsBottomBarVisible="False">
        <local:CustomTabBar.BottomLayout>
            <StackLayout>
                <Label Text="Bottom bar" />
            </StackLayout>
        </local:CustomTabBar.BottomLayout>
        <ShellContent Title="About" Icon="icon_about.png" Route="AboutPage" ContentTemplate="{views:AppDataTemplate views:AboutPage}" />
        <ShellContent Title="Browse" Icon="icon_feed.png" ContentTemplate="{views:AppDataTemplate views:ItemsPage}" />
</local:CustomTabBar>

It worked on the Android platform after extending ShellItemRenderer and implementing custom Layout, but I hit the wall on the iOS. If I have any scrollable content (ScrollView or CollectionView), my bottom bar covers some of the items: bottom bar visible, without bottom bar.

Is there a way to implement a custom layout, like on the Android platform, or to adjust existing content each time after the bottom bar appears or disappears?

Here is the implementation of CustomShellItemRenderer:

public class CustomShellItemRenderer : ShellItemRenderer
    {
        #region Fields

        private UIView BottomBar;

        #endregion Fields

        #region Constructors

        public CustomShellItemRenderer(IShellContext context) : base(context)
        {
        }

        #endregion Constructors

        protected CustomTabBar CustomTabBar => ShellItem as CustomTabBar;

        #region Methods

        public override void ViewDidLayoutSubviews()
        {
            base.ViewDidLayoutSubviews();

            var view = ConvertFormsToNative(CustomTabBar.BottomLayout, BottomBar.Frame);
            BottomBar.AddSubview(view);
            BottomBar.BackgroundColor = UIColor.Gray;
            if (!CustomTabBar.IsBottomBarVisible)
            {
                BottomBar.Hidden = true;
            }
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            TabBar.Translucent = false;
            BottomBar = new UIView();
            InitView();
        }

        private UIView ConvertFormsToNative(Xamarin.Forms.View view, CGRect size)
        {
            var renderer = Platform.CreateRenderer(view);

            renderer.NativeView.AutoresizingMask = UIViewAutoresizing.All;
            renderer.NativeView.ContentMode = UIViewContentMode.ScaleToFill;

            renderer.Element.Layout(size.ToRectangle());

            var nativeView = renderer.NativeView;

            nativeView.SetNeedsLayout();

            return nativeView;
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == nameof(CustomTabBar.IsBottomBarVisible))
            {
                if (CustomTabBar != null)
                {
                    if (CustomTabBar.IsBottomBarVisible)
                    {
                        BottomBar.Hidden = false;
                    }
                    else
                    {
                        BottomBar.Hidden = true;
                    }
                }
            }
        }

        private void InitView()
        {
            var window = (UIApplication.SharedApplication.Delegate as AppDelegate)?.Window;
            var root = window.RootViewController;

            const int height = 60;

            var realTabBarHeight = TabBar.Frame.Size.Height + (UIApplication.SharedApplication.KeyWindow?.SafeAreaInsets.Bottom ?? 34);
            var frame = new CGRect(TabBar.Frame.X, window.Frame.GetMaxY() - (realTabBarHeight + height), TabBar.Frame.Width, height);
            BottomBar.Frame = frame;
            var windowHeight = window.Frame.GetMaxY();
            window.AddSubview(BottomBar);
        }

        #endregion Methods
    }

Upvotes: 1

Views: 584

Answers (1)

ColeX
ColeX

Reputation: 14475

We can adjust the height of the content dynamically while the bottom bar appears/disappears.

Sample code

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    base.OnElementPropertyChanged(sender, e);

    if (e.PropertyName == nameof(CustomTabBar.IsBottomBarVisible))
    {
        if (CustomTabBar != null)
        {
            var window = (UIApplication.SharedApplication.Delegate as AppDelegate)?.Window;
            var root = window.RootViewController;
            var view = root.View;
            var viewframe = view.Frame;

            if (CustomTabBar.IsBottomBarVisible)
            {
               BottomBar.Hidden = false;

               viewframe.Height = viewframe.Height - bottomBarHeight;
            }
            else
            {
               BottomBar.Hidden = true;

               viewframe.Height = viewframe.Height + bottomBarHeight;
            }

            view.Frame = viewframe;
        }
    }
}

Upvotes: 0

Related Questions