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..201a6018f6b2 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutLayoutManager.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutLayoutManager.cs @@ -221,23 +221,8 @@ internal void SetHeaderContentInset() return; } - var headerBehavior = _context.Shell.FlyoutHeaderBehavior; - if (headerBehavior == FlyoutHeaderBehavior.Default || headerBehavior == FlyoutHeaderBehavior.Fixed) - { - // For Default/Fixed, the scroll view frame is positioned below the header by LayoutContent, - // so no top content inset is needed and no content offset compensation should be applied. - // Applying the compensation (offset = oldInset - 0) would incorrectly scroll the content - // down by the old inset amount, hiding the first flyout item behind the header. - ScrollView.ContentInset = new UIEdgeInsets(0, 0, 0, 0); - UpdateVerticalScrollMode(); - return; - } - else - { - // For Scroll/CollapseOnScroll, the scroll view overlaps the header so the header - // can scroll away or shrink. We use content inset to push items below it initially. - ScrollView.ContentInset = new UIEdgeInsets((nfloat)Math.Max(HeaderMinimumHeight, MeasuredHeaderViewHeightWithNoMargin), 0, 0, 0); - } + // We take the measured header height without margin, since the margin is already accounted for in the positioning of the scroll view itself. + ScrollView.ContentInset = new UIEdgeInsets((nfloat)Math.Max(HeaderMinimumHeight, MeasuredHeaderViewHeightWithNoMargin), 0, 0, 0); } else { @@ -343,19 +328,9 @@ void LayoutContent(CGRect parentBounds, nfloat footerHeight) } 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; - } - 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; - } + // For ScrollView, we need to consider the margin, but we should not consider the header height, since it should overlap with the scroll view. + // The content inset is already managed by SetHeaderContentInset. + contentYOffset += HeaderView.View.Margin.VerticalThickness; } } diff --git a/src/Controls/tests/DeviceTests/Elements/Shell/ShellFlyoutTests.cs b/src/Controls/tests/DeviceTests/Elements/Shell/ShellFlyoutTests.cs index a77d94c8afbb..76180c67934b 100644 --- a/src/Controls/tests/DeviceTests/Elements/Shell/ShellFlyoutTests.cs +++ b/src/Controls/tests/DeviceTests/Elements/Shell/ShellFlyoutTests.cs @@ -233,19 +233,18 @@ await RunShellTest(shell => var expectedContentY = headerMargin.Top + headerMargin.Bottom + contentMargin.Top; #if IOS - if (contentType == "ScrollView" && - (behavior == FlyoutHeaderBehavior.Scroll || behavior == FlyoutHeaderBehavior.CollapseOnScroll)) + if (contentType != "ScrollView") +#endif { - // For Scroll/CollapseOnScroll, the scroll view overlaps the header so the header - // can scroll away or shrink. Content is offset via ContentInset, not frame position. - var scrollViewContentInsetTop = ((UIScrollView)((IView)shell.FlyoutContent).Handler.PlatformView).ContentInset.Top; - AssertionExtensions.CloseEnough(headerFrame.Height, scrollViewContentInsetTop, message: "Content ScrollView Inset Y"); + expectedContentY += headerFrame.Height; } +#if IOS else -#endif { - expectedContentY += headerFrame.Height; + var scrollViewContentInsetTop = ((UIScrollView)((IView)shell.FlyoutContent).Handler.PlatformView).ContentInset.Top; + AssertionExtensions.CloseEnough(headerFrame.Height, scrollViewContentInsetTop, message: "Content ScrollView Inset Y"); } +#endif AssertionExtensions.CloseEnough(0, contentFrame.X, message: "Content X"); AssertionExtensions.CloseEnough(expectedContentY, contentFrame.Y, epsilon: 0.5, message: "Content Y"); @@ -261,56 +260,6 @@ await RunShellTest(shell => AssertionExtensions.CloseEnough(expectedFooterY + footerFrame.Height, flyoutFrame.Height, epsilon: 0.5, message: "Total Height"); }); } - // Regression test for https://github.com/dotnet/maui/issues/34925 - // For Default/Fixed header behavior, the scroll view should be positioned below the header - // (ContentInset.Top = 0) so items cannot scroll behind the header. - [Theory] - [InlineData(FlyoutHeaderBehavior.Default)] - [InlineData(FlyoutHeaderBehavior.Fixed)] - public async Task FlyoutScrollViewDoesNotOverlapHeaderForDefaultAndFixed(FlyoutHeaderBehavior behavior) - { - var headerHeight = 150; - - await RunShellTest(shell => - { - shell.FlyoutHeaderBehavior = behavior; - shell.FlyoutHeader = new VerticalStackLayout - { - HeightRequest = headerHeight, - BackgroundColor = Colors.Blue, - Children = { new Label { Text = "Header" } } - }; - - // Add enough items to require scrolling - for (int i = 0; i < 20; i++) - { - shell.Items.Add(new FlyoutItem - { - Title = $"Page {i}", - Items = { new ShellContent { ContentTemplate = new DataTemplate(() => new ContentPage()) } } - }); - } - }, - async (shell, handler) => - { - await OpenFlyout(handler); - - var flyoutView = GetFlyoutPlatformView(handler); - var scrollView = flyoutView.FindDescendantView(); - Assert.NotNull(scrollView); - - var headerFrame = GetFrameRelativeToFlyout(handler, (IView)shell.FlyoutHeader); - var headerBottom = headerFrame.Y + headerFrame.Height; - - // For Default/Fixed, the scroll view should be positioned below the header, - // not overlapping it with a content inset. When the scroll view overlaps the header, - // items become visible behind a semi-transparent header when scrolling. - Assert.True( - scrollView.Frame.Y >= headerBottom - 0.5, - $"ScrollView frame (Y={scrollView.Frame.Y}) should start at or below header bottom ({headerBottom}). " + - $"ContentInset.Top={scrollView.ContentInset.Top}. Items can scroll behind the header (issue #34925)."); - }); - } #endif #endif