diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs index e94287cd4aaa..563888ebfcab 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs @@ -925,4 +925,4 @@ public override bool CanScrollVertically() } } } -} \ No newline at end of file +} diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutLayoutManager.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutLayoutManager.cs index bea2a773b0c6..5f0a13a86a54 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutLayoutManager.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutLayoutManager.cs @@ -325,41 +325,51 @@ void LayoutHeader(CGRect parentFrame) void LayoutContent(CGRect parentBounds, nfloat footerHeight) { - double contentYOffset = 0; + var safeAreaInsets = UIApplication.SharedApplication.GetSafeAreaInsetsForWindow(); - if (ShouldHonorSafeArea(HeaderView?.View) || - (HeaderView is null && ShouldHonorSafeArea(Content))) + // Honor ISafeAreaView.IgnoreSafeArea and explicit margins (same as LayoutHeader) + nfloat safeAreaTop = 0; + if (ShouldHonorSafeArea(HeaderView?.View) || (HeaderView is null && ShouldHonorSafeArea(Content))) { - // We add the safe area if margin is not explicitly set. This matches the header behavior. - contentYOffset += (float)UIApplication.SharedApplication.GetSafeAreaInsetsForWindow().Top; + safeAreaTop = safeAreaInsets.Top; } + nfloat safeAreaBottom = safeAreaInsets.Bottom; + + var contentY = parentBounds.Y + safeAreaTop; + var contentHeight = parentBounds.Height - safeAreaTop - safeAreaBottom - footerHeight; if (HeaderView is not null) { if (ScrollView is null) { - // The margin is already managed by MAUI's layout system, so we don't need to add it here and we just offset the content by the header's height. - contentYOffset += HeaderView.Frame.Height; + // The margin is already managed by MAUI's layout system, so we don't need to add it here + // and we just offset the content by the header's height. + contentY += HeaderView.Frame.Height; + contentHeight -= HeaderView.Frame.Height; } else { var headerBehavior = _context.Shell.FlyoutHeaderBehavior; if (headerBehavior == FlyoutHeaderBehavior.Default || headerBehavior == FlyoutHeaderBehavior.Fixed) { - // For Default/Fixed, position the scroll view below the header so items - // cannot scroll behind it. No content inset is needed in this case. - contentYOffset += HeaderView.Frame.Height; + // For Default/Fixed, the scroll view frame is positioned below the header so items + // cannot scroll behind it. SetHeaderContentInset sets no top content inset here. + contentY += HeaderView.Frame.Height; + contentHeight -= HeaderView.Frame.Height; } else { // For Scroll/CollapseOnScroll, the scroll view overlaps the header so the header - // can scroll away or shrink. The content inset is managed by SetHeaderContentInset. - contentYOffset += HeaderView.View.Margin.VerticalThickness; + // can scroll away or shrink. The content inset is managed by SetHeaderContentInset; + // only account for margin here. + var marginOffset = (nfloat)HeaderView.View.Margin.VerticalThickness; + contentY += marginOffset; + contentHeight -= marginOffset; } } } - var contentFrame = new Rect(parentBounds.X, contentYOffset, parentBounds.Width, parentBounds.Height - contentYOffset - footerHeight); + var contentFrame = new Rect(parentBounds.X, contentY, parentBounds.Width, contentHeight); if (Content is null) { ContentView.Frame = contentFrame.AsCGRect(); diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32275.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32275.cs index f3a356b82d27..2e2fb2843b31 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32275.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32275.cs @@ -1,4 +1,3 @@ -#if ANDROID //More info : https://github.com/dotnet/maui/pull/33335 using NUnit.Framework; using UITest.Appium; using UITest.Core; @@ -7,8 +6,6 @@ namespace Microsoft.Maui.TestCases.Tests.Issues; public class Issue32275 : _IssuesUITest { - const string FlyoutItem = "FlyoutItem"; - const string ResetButton = "Reset"; public override string Issue => "Shell Flyout SafeArea Rendering"; protected override bool ResetAfterEachTest => true; @@ -34,8 +31,8 @@ public void VerifyFlyoutWithHeaderFooter() App.Tap("ToggleHeaderFooter"); App.WaitForElement("PageLoaded"); App.ShowFlyout(); - App.WaitForElement("Header"); - App.WaitForElement("Footer"); + App.WaitForElement("Header View"); + App.WaitForElement("Footer View"); VerifyScreenshot(); } @@ -62,8 +59,8 @@ public void VerifyCustomFlyoutContentTemplateWithHeaderFooter() App.Tap("ToggleHeaderFooter"); App.WaitForElement("PageLoaded"); App.ShowFlyout(); - App.WaitForElement("Header"); - App.WaitForElement("Footer"); + App.WaitForElement("Header View"); + App.WaitForElement("Footer View"); VerifyScreenshot(); } @@ -92,9 +89,8 @@ public void VerifyCustomFlyoutContentWithHeaderFooter() App.WaitForElement("PageLoaded"); App.ShowFlyout(); App.WaitForElement("ContentView"); - App.WaitForElement("Header"); - App.WaitForElement("Footer"); + App.WaitForElement("Header View"); + App.WaitForElement("Footer View"); VerifyScreenshot(); } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutOverlayResizesOnRotation.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutOverlayResizesOnRotation.png index 304631b4a0a2..660080dcfbb4 100644 Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutOverlayResizesOnRotation.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutOverlayResizesOnRotation.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentRendering.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentRendering.png new file mode 100644 index 000000000000..a4ffd4cd3e33 Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentRendering.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentTemplateRendering.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentTemplateRendering.png new file mode 100644 index 000000000000..3423fa125834 Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentTemplateRendering.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentTemplateWithHeaderFooter.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentTemplateWithHeaderFooter.png new file mode 100644 index 000000000000..d711580bb3fb Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentTemplateWithHeaderFooter.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentWithHeaderFooter.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentWithHeaderFooter.png new file mode 100644 index 000000000000..b5a90dea88ca Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentWithHeaderFooter.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyDefaultFlyoutItemsRendering.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyDefaultFlyoutItemsRendering.png new file mode 100644 index 000000000000..0fae8fd95c8a Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyDefaultFlyoutItemsRendering.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutWithHeaderFooter.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutWithHeaderFooter.png new file mode 100644 index 000000000000..f9baf61d860e Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutWithHeaderFooter.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_Height.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_Height.png index 9ec2a396541b..42b036be6c3d 100644 Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_Height.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_Height.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundColor.png index 2804b8567467..dd405133d508 100644 Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundColor.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundColor.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundImage.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundImage.png index 18d87a1fd610..4a89cddb8dd0 100644 Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundImage.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundImage.png differ