繁体   English   中英

如何在 Xamarin.Forms 中推送时隐藏标签栏?

[英]How to hide Tab Bar on push in Xamarin.Forms?

最近几天我在 iOS 上的 Xamarin.Forms 中使用TabbedPage苦苦挣扎。 我找到了一些类似的解决方案: https : //forums.xamarin.com/discussion/20901/hide-tab-bar-on-push

但是,它们中没有一个运行良好。 我还尝试将TabbedRenderer子类TabbedRenderer并将 TabBar 高度设置为 0。它有效,但如果我在NavigationPage.Pushed事件处理程序中隐藏 TabBar,则会出现一些延迟,例如 TableView 底部有空白空间。

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

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

有没有人成功地显示和隐藏 TabBar?

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

为了解决这个问题,我们只需要布局所有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();
            }
        }
    }
}  

最后结果:

自动隐藏标签栏

我尝试过的

  • 创建ContentPage的子类,并在其中创建BindableProperty(例如HidesBottomBarWhenPushed )。 我在PageRenderer中设置了ViewController.hidesBottomBarWhenPushed ,但是它不能工作,尽管我可以获得此属性的值。

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

我认为hidesBottomBarWhenPushed一定有问题,我们不能通过这种方式隐藏hidesBottomBarWhenPushed 作为一种临时且简单的解决方法,我更改了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上禁用了动画,问题消失了。

测试:

在此处输入图片说明

当我需要在屏幕上绘制标签栏之前将其隐藏时,我遇到了一个问题。

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

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

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

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

TabbedPage包裹在NavigationPage以便您的应用程序的结构变为

  • NavigationPage(根)
    • TappedPage
      • NavigationPage
        • ContentPage(带有标签栏)
    • ContentPage(无标签栏)

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

<TabbedPage
    ...
    HasNavigationBar="False"> 

如果使用“根” NavigationPage推送页面,则选项卡NavigationPage将被隐藏,并且底部没有空格。

参见我的示例: https : //github.com/Jfcobuss/HideTabbarExample/tree/master/HideTabbarExample

我确实设法使用HidesBottomBarWhenPushed属性以更“原生的方式”获得了预期的结果。 如果您查看 iOS 的导航是如何实现的,您会注意到,在将新的视图控制器推送到导航堆栈之前, 它被包装在CreateViewControllerForPage方法内的父视图控制器中 它让我有机会拦截尚未推送到导航堆栈的已创建视图控制器并设置HidesBottomBarWhenPushed属性。 为此,我创建了一个从ContentPage派生的空类:

public class NoTabBarPage : ContentPage { }

所以现在从NoTabBarPage派生的每个页面都没有底部标签栏。

还有一个使一切正常的渲染器:

[assembly: ExportRenderer(typeof(NoTabBarPage), typeof(NoTabBarPageRenderer))]
namespace HideTabBar.iOS.Renderers
{
    public class NoTabBarPageRenderer : PageRenderer
    {
        public override void DidMoveToParentViewController(UIViewController parent)
        {
            base.DidMoveToParentViewController(parent);

            parent.HidesBottomBarWhenPushed = true;
        }

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

            if (View != null && View.Superview != null && View.Frame.Height != View.Superview.Frame.Height)
            {
                View.Frame = View.Superview.Frame;
                Element.Layout(View.Frame.ToRectangle());
            }
        }
    }
}

像往常一样,它不能完美地使用Xamarin.Forms ,结果证明,子视图控制器没有拉伸到父视图控制器的框架。 而且我必须在ViewDidLayoutSubviews手动修复。

在此处输入图片说明

还要注意,每个新的视图控制器在带有隐藏标签栏的视图控制器之后推送到导航堆栈也没有标签栏。 因此,在我的情况下,每个“详细信息”页面都源自NoTabBarPage

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM