From 1f6228c12bf09ae9fae1938d3cae3aa308bce655 Mon Sep 17 00:00:00 2001 From: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com> Date: Wed, 9 Jul 2025 02:17:04 +0200 Subject: [PATCH 1/5] [iOS][Shell]Tab becomes blank after specific navigation pattern - fix (#29825) * [iOS][Shell]Tab becomes blank after specific navigation pattern * Added UI Test * CR changes * Update ShellItemRenderer.cs * - fix long press back navigation * Update ShellSectionRenderer.cs * Update ShellSectionRenderer.cs * Update ShellItemRenderer.cs * Update ShellItemRenderer.cs * Update ShellSectionRenderer.cs * Update src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs Co-authored-by: Rui Marinho * Update src/Controls/src/Core/Shell/ShellSection.cs Co-authored-by: Rui Marinho * - apply review updates --------- Co-authored-by: Shane Neuville Co-authored-by: Rui Marinho --- .../Handlers/Shell/iOS/ShellItemRenderer.cs | 2 +- .../Shell/iOS/ShellSectionRenderer.cs | 45 ++++++++++++----- src/Controls/src/Core/Shell/ShellSection.cs | 37 ++++++++++++++ .../TestCases.HostApp/Issues/Issue29798.cs | 49 +++++++++++++++++++ .../Tests/Issues/Issue29798.cs | 31 ++++++++++++ 5 files changed, 150 insertions(+), 14 deletions(-) create mode 100644 src/Controls/tests/TestCases.HostApp/Issues/Issue29798.cs create mode 100644 src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29798.cs diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellItemRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellItemRenderer.cs index a55b024f926f..7108de162c0b 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellItemRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellItemRenderer.cs @@ -114,7 +114,7 @@ public override void ViewDidLoad() { bool accept = true; var r = RendererForViewController(viewController); - if (r != null) + if (r is not null) accept = ((IShellItemController)ShellItem).ProposeSection(r.ShellSection, false); return accept; diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs index ac7a7ff248f9..2f2d90e48f85 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs @@ -70,8 +70,6 @@ void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance) ShellSection _shellSection; bool _ignorePopCall; - bool _popRequested; - // When setting base.ViewControllers iOS doesn't modify the property right away. // if you set base.ViewControllers to a new array and then retrieve base.ViewControllers // iOS will return the previous array until the new array has been processed @@ -109,7 +107,37 @@ public bool ShouldPopItem(UINavigationBar _, UINavigationItem __) [Export("navigationBar:didPopItem:")] [Internals.Preserve(Conditional = true)] bool DidPopItem(UINavigationBar _, UINavigationItem __) - => _popRequested || SendPop(); + { + // Check for null references + if (_shellSection?.Stack is null || NavigationBar?.Items is null) + return true; + + // Check if stacks are in sync + if (_shellSection.Stack.Count == NavigationBar.Items.Length) + return true; + + var pages = _shellSection.Stack.ToList(); + + // Ensure we have enough pages and navigation items + if (pages.Count == 0 || NavigationBar.Items.Length == 0) + return true; + + // Bounds check: ensure we have a valid index for pages array + int targetIndex = NavigationBar.Items.Length - 1; + if (targetIndex < 0 || targetIndex >= pages.Count) + return true; + + _shellSection.SyncStackDownTo(pages[targetIndex]); + + for (int i = pages.Count - 1; i >= NavigationBar.Items.Length; i--) + { + var page = pages[i]; + if (page != null) + DisposePage(page); + } + + return true; + } internal bool SendPop() { @@ -394,7 +422,6 @@ protected virtual void OnNavigationRequested(object sender, NavigationRequestedE protected virtual async void OnPopRequested(NavigationRequestedEventArgs e) { - _popRequested = true; var page = e.Page; var animated = e.Animated; @@ -435,7 +462,6 @@ async void ProcessPopToRoot() protected virtual async void OnPopToRootRequested(NavigationRequestedEventArgs e) { - _popRequested = true; var animated = e.Animated; var task = new TaskCompletionSource(); var pages = _shellSection.Stack.ToList(); @@ -528,12 +554,7 @@ void DisposePage(Page page, bool calledFromDispose = false) _trackers.Remove(page); } - - var renderer = page.Handler; - if (renderer != null) - { - renderer.DisconnectHandler(); - } + page?.DisconnectHandlers(); } Element ElementForViewController(UIViewController viewController) @@ -597,7 +618,6 @@ public override UIViewController[] PopToViewController(UIViewController viewCont public override void PushViewController(UIViewController viewController, bool animated) { _pendingViewControllers = null; - _popRequested = false; if (IsInMoreTab && ParentViewController is UITabBarController tabBarController) { tabBarController.MoreNavigationController.PushViewController(viewController, animated); @@ -610,7 +630,6 @@ public override void PushViewController(UIViewController viewController, bool an public override UIViewController PopViewController(bool animated) { - _popRequested = true; _pendingViewControllers = null; if (IsInMoreTab && ParentViewController is UITabBarController tabBarController) { diff --git a/src/Controls/src/Core/Shell/ShellSection.cs b/src/Controls/src/Core/Shell/ShellSection.cs index 5f648989cde7..dcf7b73dc85a 100644 --- a/src/Controls/src/Core/Shell/ShellSection.cs +++ b/src/Controls/src/Core/Shell/ShellSection.cs @@ -111,6 +111,43 @@ void IShellSectionController.SendInsetChanged(Thickness inset, double tabThickne _lastTabThickness = tabThickness; } + internal void SyncStackDownTo(Page page) + { + if (_navStack.Count <= 1) + { + throw new Exception("Nav Stack consistency error"); + } + + var oldStack = _navStack; + + int index = oldStack.IndexOf(page); + _navStack = new List(); + + // Rebuild the stack up to the page that was passed in + // Since this now represents the current accurate stack + for (int i = 0; i <= index; i++) + { + _navStack.Add(oldStack[i]); + } + + // Send Disappearing for all pages that are no longer in the stack + // This will really only SendDisappearing on the top page + // but we just call it on all of them to be sure + for (int i = oldStack.Count - 1; i > index; i--) + { + oldStack[i].SendDisappearing(); + } + + UpdateDisplayedPage(); + + for (int i = index + 1; i < oldStack.Count; i++) + { + RemovePage(oldStack[i]); + } + + (Parent?.Parent as IShellController)?.UpdateCurrentState(ShellNavigationSource.Pop); + } + async void IShellSectionController.SendPopping(Task poppingCompleted) { if (_navStack.Count <= 1) diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue29798.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue29798.cs new file mode 100644 index 000000000000..afa067d363d2 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue29798.cs @@ -0,0 +1,49 @@ +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 29798, "Tab becomes blank after specific navigation pattern", PlatformAffected.iOS)] +public class Issue29798 : Shell +{ + public Issue29798() + { + Routing.RegisterRoute(nameof(Issue29798Page), typeof(Issue29798Page)); + + Items.Add(new ShellContent() + { + Title = "Tab1", + Content = new ContentPage() + { + Title = "Test", + Content = new Button + { + Text = "Go to Page 2", + AutomationId = "GotoPage2", + Command = new Command(async () => await Current.GoToAsync(nameof(Issue29798Page))) + } + } + }); + + Items[0].Items.Add(new ShellContent() + { + Content = new ContentPage() + }); + } +} + +public class Issue29798Page : ContentPage +{ + public Issue29798Page() + { + Title = "Page 2"; + Content = new StackLayout + { + Children = + { + new Label + { + AutomationId = "Page2Label", + Text = "Welcome to Page 2" + } + } + }; + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29798.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29798.cs new file mode 100644 index 000000000000..aed55aa44b85 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29798.cs @@ -0,0 +1,31 @@ +#if IOS +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue29798 : _IssuesUITest +{ + public Issue29798(TestDevice testDevice) : base(testDevice) + { + } + + public override string Issue => "Tab becomes blank after specific navigation pattern"; + + [Test] + [Category(UITestCategories.Shell)] + public void TabShouldNotBeBlackAfterTabNavigation() + { + App.WaitForElement("GotoPage2"); + App.Tap("GotoPage2"); + App.WaitForElement("Page2Label"); + App.Tap("Tab1"); + App.WaitForElement("GotoPage2"); + App.Tap("GotoPage2"); + App.WaitForElement("Page2Label"); + App.Tap("Tab1"); + App.WaitForElement("GotoPage2"); + } +} +#endif \ No newline at end of file From c65f4338790fe79ab68df5551a0797ccf4c18bda Mon Sep 17 00:00:00 2001 From: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com> Date: Wed, 9 Jul 2025 03:38:50 +0200 Subject: [PATCH 2/5] MauiScrollView resets ContentOffset on first layout pass - fix (#30453) * MauiScrollView resets ContentOffset on first layout pass - fix * Update MauiScrollView.cs --- src/Core/src/Platform/iOS/MauiScrollView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/src/Platform/iOS/MauiScrollView.cs b/src/Core/src/Platform/iOS/MauiScrollView.cs index 410871c7bae9..563131738036 100644 --- a/src/Core/src/Platform/iOS/MauiScrollView.cs +++ b/src/Core/src/Platform/iOS/MauiScrollView.cs @@ -56,7 +56,7 @@ public override void LayoutSubviews() // For Right-To-Left (RTL) layouts, we need to adjust the content arrangement and offset // to ensure the content is correctly aligned and scrolled. This involves a second layout // arrangement with an adjusted starting point and recalculating the content offset. - if (_previousEffectiveUserInterfaceLayoutDirection != EffectiveUserInterfaceLayoutDirection) + if (_previousEffectiveUserInterfaceLayoutDirection is not null && _previousEffectiveUserInterfaceLayoutDirection != EffectiveUserInterfaceLayoutDirection) { if (EffectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirection.RightToLeft) { From 1a14e05e21e850249193c86ba94fffd00ef9b43a Mon Sep 17 00:00:00 2001 From: Anandhan Rajagopal <97146406+anandhan-rajagopal@users.noreply.github.com> Date: Wed, 9 Jul 2025 16:49:45 +0530 Subject: [PATCH 3/5] Update Geocoding_Tests.cs --- src/Essentials/test/DeviceTests/Tests/Geocoding_Tests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Essentials/test/DeviceTests/Tests/Geocoding_Tests.cs b/src/Essentials/test/DeviceTests/Tests/Geocoding_Tests.cs index 2aa9f9bab6d5..0b6c231d2e28 100644 --- a/src/Essentials/test/DeviceTests/Tests/Geocoding_Tests.cs +++ b/src/Essentials/test/DeviceTests/Tests/Geocoding_Tests.cs @@ -15,7 +15,10 @@ public Geocoding_Tests() ApplicationModel.Platform.MapServiceToken = "RJHqIE53Onrqons5CNOx~FrDr3XhjDTyEXEjng-CRoA~Aj69MhNManYUKxo6QcwZ0wmXBtyva0zwuHB04rFYAPf7qqGJ5cHb03RCDw1jIW8l"; #endif } -#if !ANDROID + +// Temporarily disabling this test on Windows due to consistent CI failures. +// See https://github.com/dotnet/maui/issues/30507 for tracking re-enablement. +#if !ANDROID && !WINDOWS [Theory] [InlineData(47.673988, -122.121513)] public async Task Get_Placemarks_LatLong(double latitude, double longitude) From d89d82ac6c821ee712da8881476bb87d739cc4a0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 08:05:04 -0500 Subject: [PATCH 4/5] [main] Workaround REVOCATION in .NET 10 by setting DOTNET_SYSTEM_NET_SECURITY_NOREVOCATIONCHECKBYDEFAULT (#30514) * Initial plan * Add DOTNET_SYSTEM_NET_SECURITY_NOREVOCATIONCHECKBYDEFAULT environment variable Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> * Update copilot-setup-steps.yml --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> Co-authored-by: Shane Neuville --- .github/workflows/copilot-setup-steps.yml | 1 + eng/cake/dotnet.cake | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 0456cb35345c..7e14e2464ca1 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -35,6 +35,7 @@ jobs: echo "$GITHUB_WORKSPACE/.dotnet" >> "$GITHUB_PATH" echo "DOTNET_ROOT=$GITHUB_WORKSPACE/.dotnet" >> "$GITHUB_ENV" echo "DOTNET_INSTALL_DIR=$GITHUB_WORKSPACE/.dotnet" >> "$GITHUB_ENV" + echo "DOTNET_SYSTEM_NET_SECURITY_NOREVOCATIONCHECKBYDEFAULT=true" >> "$GITHUB_ENV" - name: Ensure we are using the correct .NET shell: pwsh diff --git a/eng/cake/dotnet.cake b/eng/cake/dotnet.cake index d341473b912a..584ba0a72f46 100644 --- a/eng/cake/dotnet.cake +++ b/eng/cake/dotnet.cake @@ -607,6 +607,7 @@ Dictionary GetDotNetEnvironmentVariables() envVariables.Add("DOTNET_ROOT", dotnet); envVariables.Add("DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR", dotnet); envVariables.Add("DOTNET_MULTILEVEL_LOOKUP", "0"); + envVariables.Add("DOTNET_SYSTEM_NET_SECURITY_NOREVOCATIONCHECKBYDEFAULT", "true"); envVariables.Add("MSBuildEnableWorkloadResolver", "true"); var existingPath = EnvironmentVariable("PATH"); @@ -629,6 +630,7 @@ void SetDotNetEnvironmentVariables(string dotnetDir = null) SetEnvironmentVariable("DOTNET_ROOT", dotnet); SetEnvironmentVariable("DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR", dotnet); SetEnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0"); + SetEnvironmentVariable("DOTNET_SYSTEM_NET_SECURITY_NOREVOCATIONCHECKBYDEFAULT", "true"); SetEnvironmentVariable("MSBuildEnableWorkloadResolver", "true"); SetEnvironmentVariable("PATH", dotnet, prepend: true); From d145ee87af84c2b4646d676ea8bf53be0204521c Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Wed, 9 Jul 2025 16:53:10 +0100 Subject: [PATCH 5/5] [ci] Update dotnet eng arcade (#30498) --- eng/Version.Details.xml | 32 +++++++++++++-------------- eng/Versions.props | 12 +++++----- eng/common/core-templates/job/job.yml | 4 ++++ eng/common/internal/NuGet.config | 3 +++ global.json | 6 ++--- 5 files changed, 32 insertions(+), 25 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1877bfeae008..937bd14d32c7 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -14,37 +14,37 @@ - + https://github.com/dotnet/arcade - 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 + 13b20849f8294593bf150a801cab639397e6c29d - + https://github.com/dotnet/arcade - 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 + 13b20849f8294593bf150a801cab639397e6c29d - + https://github.com/dotnet/arcade - 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 + 13b20849f8294593bf150a801cab639397e6c29d - + https://github.com/dotnet/arcade - 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 + 13b20849f8294593bf150a801cab639397e6c29d - + https://github.com/dotnet/arcade - 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 + 13b20849f8294593bf150a801cab639397e6c29d - + https://github.com/dotnet/arcade - 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 + 13b20849f8294593bf150a801cab639397e6c29d - + https://github.com/dotnet/arcade - 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 + 13b20849f8294593bf150a801cab639397e6c29d - + https://github.com/dotnet/arcade - 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 + 13b20849f8294593bf150a801cab639397e6c29d diff --git a/eng/Versions.props b/eng/Versions.props index f2e193f23ebb..9c97a0f4f3b0 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -135,13 +135,13 @@ 0.9.0 4.2.3 9.0.0 - 9.0.0-beta.25302.2 - 9.0.0-beta.25302.2 - 9.0.0-beta.25302.2 - 9.0.0-beta.25302.2 + 9.0.0-beta.25325.4 + 9.0.0-beta.25325.4 + 9.0.0-beta.25325.4 + 9.0.0-beta.25325.4 1.1.87-gba258badda - 9.0.0-beta.25302.2 - 9.0.0-beta.25302.2 + 9.0.0-beta.25325.4 + 9.0.0-beta.25325.4 17.6.0 diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml index ba53ebfbd513..abe80a2a0e09 100644 --- a/eng/common/core-templates/job/job.yml +++ b/eng/common/core-templates/job/job.yml @@ -134,6 +134,10 @@ jobs: signType: $(_SignType) zipSources: false feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + ConnectedPMEServiceName: 6cc74545-d7b9-4050-9dfa-ebefcc8961ea + ${{ else }}: + ConnectedPMEServiceName: 248d384a-b39b-46e3-8ad5-c2c210d5e7ca env: TeamName: $(_TeamName) MicroBuildOutputFolderOverride: '$(Agent.TempDirectory)' diff --git a/eng/common/internal/NuGet.config b/eng/common/internal/NuGet.config index 19d3d311b166..f70261ed689b 100644 --- a/eng/common/internal/NuGet.config +++ b/eng/common/internal/NuGet.config @@ -4,4 +4,7 @@ + + + diff --git a/global.json b/global.json index d777bc0e74b4..5f386a47311e 100644 --- a/global.json +++ b/global.json @@ -1,12 +1,12 @@ { "tools": { - "dotnet": "9.0.105" + "dotnet": "9.0.107" }, "msbuild-sdks": { "MSBuild.Sdk.Extras": "3.0.44", "Microsoft.Build.NoTargets": "3.7.0", - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25302.2", - "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.25302.2" + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25325.4", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.25325.4" }, "sdk": { "allowPrerelease": false