簡體   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