Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,12 @@ protected virtual void UpdateTabBarVisible()
return;
}

var tabBarVisible =
(Page.FindParentOfType<ShellItem>() as IShellItemController)?.ShowTabs ?? Shell.GetTabBarIsVisible(Page);

ViewController.HidesBottomBarWhenPushed = !tabBarVisible;
var tabBarVisible = (Page.FindParentOfType<ShellItem>() as IShellItemController)?.ShowTabs ?? Shell.GetTabBarIsVisible(Page);
// In iOS 18, the tab bar visibility is effectively managed by the TabBarHidden property in ShellItemRenderer.
if (!(OperatingSystemMacCatalyst18Workaround.IsMacCatalystVersionAtLeast18() || OperatingSystem.IsIOSVersionAtLeast(18)))
{
ViewController.HidesBottomBarWhenPushed = !tabBarVisible;
}
}

void OnToolbarPropertyChanged(object sender, PropertyChangedEventArgs e)
Expand Down
21 changes: 16 additions & 5 deletions src/Controls/src/Core/Shell/ShellSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -410,16 +410,27 @@ async Task PrepareCurrentStackForBeingReplaced(ShellNavigationRequest request, S
continue;
}

// This is the page that we will eventually get to once we've finished
// modifying the existing navigation stack
// So we want to fire appearing on it
navPage.SendAppearing();

// We use this inside ModalNavigationManager to indicate that we're going to be popping multiple
// modal pages so we don't want it to fire any appearing events
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we do something in the UI from the UITest, like update a Label Text, to validate the correct behavior with the Appearing method?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This existing test covers the modal scenario here

  • PoppingModalStackFiresAppearingOnRevealedModalPage

I've added

  • PoppingModalStackFiresAppearingOnRevealedNonModalPage

To cover popping multiple pages down to a shell page to ensure appearing still fires

IsPoppingModalStack = true;

while (navStack.Count > popCount && Navigation.ModalStack.Count > 0)
{
bool isAnimated = animate ?? IsNavigationAnimated(navStack[navStack.Count - 1]);

var nextModalPageToPopIndex = navStack.Count - 2;
// Check if we've reached the last modal page to pop before revealing the target modal page.
// The IsPoppingModalStack flag instructs ModalNavigationManager to suppress lifecycle events
// during a bulk pop operation.
// This approach is required because the standard modal APIs do not support popping multiple
// pages at once, while Shell URI navigation does.
// Ideally, this logic would be refactored into ModalNavigationManager to expose batch popping
// via the INavigation APIs.
if (nextModalPageToPopIndex >= 0 && navStack[nextModalPageToPopIndex] == navPage)
{
IsPoppingModalStack = false;
}

if (Navigation.ModalStack.Contains(navStack[navStack.Count - 1]))
{
await PopModalAsync(isAnimated);
Expand Down
12 changes: 12 additions & 0 deletions src/Controls/tests/Core.UnitTests/ShellModalTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,18 @@ public async Task PoppingModalStackFiresAppearingOnRevealedModalPage()
Assert.True(true);
}

[Fact]
public async Task PoppingModalStackFiresAppearingOnRevealedNonModalPage()
{
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellContentRoute: "MainContent"));

await shell.GoToAsync($"LifeCyclePage/ContentPage/ModalTestPage2/ModalTestPage");
var lifeCyclePage = shell.Descendants().OfType<ShellLifeCycleTests.LifeCyclePage>().First();
Assert.False(lifeCyclePage.Appearing);
await shell.GoToAsync($"../../..");
Assert.True(lifeCyclePage.Appearing);
}

[Fact]
public async Task ModalPopsWhenSwitchingShellContent()
Expand Down
135 changes: 135 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue26598.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using Microsoft.Maui.Controls;

namespace Maui.Controls.Sample.Issues;
[Issue(IssueTracker.Github, 26598, "Tabbar disappears when navigating back from page with hidden TabBar in iOS", PlatformAffected.iOS)]
public class Issue26598 : TestShell
{
TabBar tabBar = new TabBar();

// Create first ShellContent
ShellContent homeShellContent = new ShellContent
{
ContentTemplate = new DataTemplate(() => new Issue26598Home()),
Title = "HomeTab",
AutomationId = "Issue26598Home",
Route = nameof(Issue26598Home)
};

// Create second ShellContent
ShellContent recentShellContent = new ShellContent
{
ContentTemplate = new DataTemplate(() => new Issue26598Recent()),
Title = "RecentTab",
Route = nameof(Issue26598Recent)
};

protected override void Init()
{
Routing.RegisterRoute("Issue26598Inner", typeof(Issue26598Inner));
Routing.RegisterRoute(nameof(Issue26589NonTab), typeof(Issue26589NonTab));
tabBar.Items.Add(homeShellContent);
tabBar.Items.Add(recentShellContent);
Items.Add(tabBar);
}

public class Issue26598Home : ContentPage
{
VerticalStackLayout stackLayout;
Button button;
public Issue26598Home()
{
Title = "Home";
HeightRequest = 200;
stackLayout = new VerticalStackLayout();
button = new Button()
{
Text = "Navigate to InnerTab",
AutomationId = "NavigateToInnerTab",
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center,
};
button.Clicked += Button_OnClicked;
stackLayout.Add(button);
Shell.SetTabBarIsVisible(this, false);
this.Content = stackLayout;
}

private void Button_OnClicked(object sender, EventArgs e)
{
Shell.Current.GoToAsync(nameof(Issue26598Inner));
}

}

public class Issue26598Inner : ContentPage
{
VerticalStackLayout stackLayout;
Button button;
public Issue26598Inner()
{
Title = "InnerTab";
stackLayout = new VerticalStackLayout();
button = new Button()
{
Text = "Navigate to TabBarPage",
AutomationId = "NavigateToTabBarPage",
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center,
};
button.Clicked += Button_OnClicked;
stackLayout.Add(button);
Shell.SetTabBarIsVisible(this, true);
this.Content = stackLayout;
}

private void Button_OnClicked(object sender, EventArgs e)
{
Shell.Current.GoToAsync(nameof(Issue26589NonTab));
}

}

public class Issue26598Recent : ContentPage
{
VerticalStackLayout stackLayout;
Label label;
public Issue26598Recent()
{
Title = "Recent";
HeightRequest = 200;

stackLayout = new VerticalStackLayout();
label = new Label()
{
Text = "Page Loaded in Recent Tab",
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center,

};
stackLayout.Add(label);
Shell.SetTabBarIsVisible(this, true);
this.Content = stackLayout;
}
}
public class Issue26589NonTab : ContentPage
{
VerticalStackLayout stackLayout;
Label label1;
public Issue26589NonTab()
{
Title = "NoTabBarPage";
stackLayout = new VerticalStackLayout();
label1 = new Label()
{
Text = "This is Non TabBarPage",
AutomationId = "Issue26589NonTab",
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center,
};
stackLayout.Add(label1);
Shell.SetTabBarIsVisible(this, false);
this.Content = stackLayout;
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using NUnit.Framework;
using NUnit.Framework.Legacy;
using UITest.Appium;
using UITest.Core;
namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue26598 : _IssuesUITest
{
public override string Issue => "Tabbar disappears when navigating back from page with hidden TabBar in iOS";

#if ANDROID
const string back = "";
#else
const string back = "InnerTab";
#endif

public Issue26598(TestDevice device) : base(device)
{
}

[Test]
[Category(UITestCategories.Shell)]
public void TabBarShouldbeVisibleNavigatingBackFromNonTabbedPage()
{
// Is a iOS issue; see https://github.com/dotnet/maui/issues/26598
// Initially TabBar for Issue26598Home is hidden
App.WaitForElement("NavigateToInnerTab");
App.Click("NavigateToInnerTab");

// Case 1 - After navigating to Inner Page , the TabBar should be visible
App.WaitForElement("RecentTab");

// Case 2 - Navigate to the InnerTabPage where the TabBar is hidden
App.WaitForElement("NavigateToTabBarPage");
App.Click("NavigateToTabBarPage");
App.WaitForElement("Issue26589NonTab");

// Case 3 - Navigate back to the HomeTab, the TabBar should be visible
App.TapBackArrow(back);
App.WaitForElement("RecentTab");
App.Click("RecentTab");
App.WaitForElement("HomeTab");
App.Click("HomeTab");
App.WaitForElement("RecentTab");
}
}