如何在Xamarin.Forms中隐藏Tab Bar?

时间:2017-09-08 15:14:44

标签: ios xamarin xamarin.ios xamarin.forms uitabbar

我在iOS上的Xamarin.Forms中使用TabbedPage进行了最近几天的努力。我发现了一些类似的解决方案: https://forums.xamarin.com/discussion/20901/hide-tab-bar-on-push

然而,它们都不是很好。我还试图将TabbedRenderer子类化并将TabBar高度设置为0.它可以工作,但如果我在NavigationPage.Pushed事件处理程序中隐藏TabBar,则会有一些延迟,例如TableView底部有空白。

如果我尝试覆盖NavigationRenderer并隐藏/显示PushViewController / PopViewController方法中的标签栏,则有时会失败。例如,如果我来回快速导航,则不会调用方法PopViewController,导致NavigationStack损坏,并且不会恢复标签栏。

我认为唯一可行的解​​决办法就是让这个属性起作用:UIViewController.HidesBottomBarWhenPushed。但是,我不知道该怎么做,因为在渲染器中设置/覆盖它不起作用。

有没有人设法成功展示&隐藏TabBar?

4 个答案:

答案 0 :(得分:4)

我设法实现了一种解决方案,该解决方案在隐藏TabBar之后用空格解决了该问题。您可以在this article中阅读有关它的更多详细信息。

要解决该问题,我们只需要布局所有ChildViewControllers。这是我的自定义TabbedPage及其TabbedPageRenderer的示例实现。

HideableTabbedPage.cs:

using System;
using Xamarin.Forms;

namespace HideTabBar.Controls
{
    public class HideableTabbedPage : TabbedPage
    {
        public static readonly BindableProperty IsHiddenProperty =
            BindableProperty.Create(nameof(IsHidden), typeof(bool), typeof(HideableTabbedPage), false);

        public bool IsHidden
        {
            get { return (bool)GetValue(IsHiddenProperty); }
            set { SetValue(IsHiddenProperty, value); }
        }
    }
}

HideableTabbedPageRenderer.cs:

using System;
using System.ComponentModel;
using System.Threading.Tasks;
using HideTabBar.Controls;
using HideTabBar.iOS.CustomRenderer;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(HideableTabbedPage), typeof(HideableTabbedPageRenderer))]
namespace HideTabBar.iOS.CustomRenderer
{
    public class HideableTabbedPageRenderer : TabbedRenderer
    {
        private bool disposed;
        private const int TabBarHeight = 49;

        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            if (e.OldElement == null)
            {
                this.Tabbed.PropertyChanged += Tabbed_PropertyChanged;
            }
        }

        private void Tabbed_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == HideableTabbedPage.IsHiddenProperty.PropertyName)
            {
                this.OnTabBarHidden((this.Element as HideableTabbedPage).IsHidden);
            }
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            this.disposed = true;
        }

        private async void OnTabBarHidden(bool isHidden)
        {
            if (this.disposed || this.Element == null || this.TabBar == null)
            {
                return;
            }

            await this.SetTabBarVisibility(isHidden);
        }

        private async Task SetTabBarVisibility(bool hide)
        {
            this.TabBar.Opaque = false;
            if (hide)
            {
                this.TabBar.Alpha = 0;
            }

            this.UpdateFrame(hide);

            // Show / Hide TabBar
            this.TabBar.Hidden = hide;
            this.RestoreFonts();

            // Animate appearing 
            if (!hide)
            {
                await UIView.AnimateAsync(0.2f, () => this.TabBar.Alpha = 1);
            }
            this.TabBar.Opaque = true;

            this.ResizeViewControllers();
            this.RestoreFonts();
        }

        private void UpdateFrame(bool isHidden)
        {
            var tabFrame = this.TabBar.Frame;
            tabFrame.Height = isHidden ? 0 : TabBarHeight;
            this.TabBar.Frame = tabFrame;
        }

        private void RestoreFonts()
        {
            // Workaround to restore custom fonts:

            foreach (var item in this.TabBar.Items)
            {
                var text = item.Title;
                item.Title = "";
                item.Title = text;
            }
        }

        private void ResizeViewControllers()
        {
            foreach (var child in this.ChildViewControllers)
            {
                child.View.SetNeedsLayout();
                child.View.SetNeedsDisplay();
            }
        }
    }
}  

最终结果:

auto hiding tab bar

答案 1 :(得分:2)

我尝试过:

  • 创建ContentPage的子类并在其中创建BindableProperty(如HidesBottomBarWhenPushed)。我在PageRenderer中设置ViewController.hidesBottomBarWhenPushed但它不起作用,虽然我可以获得此属性的值。

  • 在PageRenderer的初始构造函数中设置this.hidesBottomBarWhenPushed,仍然没有运气。

我认为hidesBottomBarWhenPushed一定有问题,我们无法通过这种方式隐藏tabbar。作为一种临时且简单的解决方法,我更改了PageRenderer中TabBarController.TabBar的可见性

class PageiOS : PageRenderer
{
    public override void ViewWillAppear(bool animated)
    {
        base.ViewWillAppear(animated);
        if (this.NavigationController != null && this.TabBarController != null)
        {
            bool isRootVC = this.NavigationController.ViewControllers.Length == 1;
            ParentViewController.TabBarController.TabBar.Hidden = !isRootVC;
        }
    }
}

它的行为与您上面说的一样,底部有一些延迟和空白。我在push和pop上禁用动画,问题就消失了。

测试:

enter image description here

答案 2 :(得分:0)

我在将选项卡栏绘制到屏幕上之前需要隐藏它时遇到了一个问题。

Wojciech Kulik的解决方案对我有所帮助,但导航到选项卡式页面时它开始闪烁。

以下代码解决了我的问题。希望对您有帮助。 将其放在TabbedRenderer派生类中

public override void ViewWillLayoutSubviews()
{
    OnTabBarHidden(true); // Hide before the page appear
}

答案 3 :(得分:0)

有一种不需要任何渲染的解决方案,并且可以在Android和iOS上使用。

TabbedPage包裹在NavigationPage中,这样您的应用程序的结构就会变得

  • NavigationPage(根)
    • 已点击页面
      • 导航页面
        • ContentPage(带有标签栏)
    • ContentPage(无标签栏)

在TabbedPage上,您必须隐藏“根”导航页的导航栏,否则,您将有2个导航栏。

<TabbedPage
    ...
    HasNavigationBar="False"> 

如果您使用'root'NavigationPage推送页面,则标签栏将被隐藏,并且底部没有空格。

在以下位置查看我的示例: https://github.com/Jfcobuss/HideTabbarExample/tree/master/HideTabbarExample