From 5639e243fb85b209acffce174b9c965bc32be5c5 Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Thu, 26 Mar 2026 17:01:13 +0100 Subject: [PATCH 1/5] Fix CollectionView not scrolling to top on iOS status bar tap On iOS, tapping the status bar should scroll the topmost UIScrollView to the top. This was not working for CollectionView, particularly inside Shell. Root cause: iOS disables scroll-to-top when multiple UIScrollViews in the view hierarchy have scrollsToTop=true. The Shell flyout's internal AccessibilityNeutralTableView (UITableView) defaults scrollsToTop to true, conflicting with the CollectionView's UICollectionView. Fix: - Set CollectionView.ScrollsToTop = true in ItemsViewController2.ViewDidLoad() to explicitly opt in to scroll-to-top behavior - Set ScrollsToTop = false on Shell's AccessibilityNeutralTableView to prevent the multi-scroll-view conflict Fixes #19866 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Handlers/Shell/iOS/ShellTableViewController.cs | 1 + .../src/Core/Handlers/Items2/iOS/ItemsViewController2.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellTableViewController.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellTableViewController.cs index dac39312fcbc..fee6f50e0e9c 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellTableViewController.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellTableViewController.cs @@ -109,6 +109,7 @@ internal class AccessibilityNeutralTableView : UITableView, IUIAccessibilityCont public AccessibilityNeutralTableView() { this.SetAccessibilityContainerType(UIAccessibilityContainerType.None); + ScrollsToTop = false; } } diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs index 674ce2f5dfad..cf580bd93dbf 100644 --- a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs +++ b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs @@ -181,6 +181,8 @@ public override void ViewDidLoad() CollectionView.ContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentBehavior.Never; } + CollectionView.ScrollsToTop = true; + RegisterViewTypes(); EnsureLayoutInitialized(); From d543d0bcd82eb64939df3314db93ec3082363383 Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Fri, 3 Apr 2026 11:06:13 +0200 Subject: [PATCH 2/5] Add device tests for ScrollsToTop fix - CollectionView test: verify UICollectionView has ScrollsToTop enabled - Shell test: verify flyout table view has ScrollsToTop disabled to prevent multi-scroll-view conflict Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../CollectionView/CollectionViewTests.iOS.cs | 23 +++++++++++++++ .../Elements/Shell/ShellTests.iOS.cs | 28 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs b/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs index 1a9dfd28b7ce..9a787065e05c 100644 --- a/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs +++ b/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs @@ -310,6 +310,29 @@ public object LoadDataTemplate() } } + [Fact] + public async Task CollectionViewScrollsToTopIsEnabled() + { + SetupBuilder(); + + var collectionView = new CollectionView + { + ItemsSource = Enumerable.Range(0, 20).Select(i => $"Item {i}").ToList(), + ItemTemplate = new DataTemplate(() => + { + var label = new Label(); + label.SetBinding(Label.TextProperty, "."); + return label; + }) + }; + + await CreateHandlerAndAddToWindow(collectionView, handler => + { + var uiCollectionView = handler.Controller.CollectionView; + Assert.True(uiCollectionView.ScrollsToTop, "CollectionView's UICollectionView should have ScrollsToTop enabled"); + }); + } + Rect GetCollectionViewCellBounds(IView cellContent) { if (!cellContent.ToPlatform().IsLoaded()) diff --git a/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.iOS.cs b/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.iOS.cs index a7b8fb032e3d..2cc5b7681676 100644 --- a/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.iOS.cs +++ b/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.iOS.cs @@ -619,6 +619,34 @@ async Task TapToSelect(ContentPage page) await OnNavigatedToAsync(page); } + [Fact(DisplayName = "Shell Flyout Table View Has ScrollsToTop Disabled")] + public async Task ShellFlyoutTableViewScrollsToTopIsDisabled() + { + SetupBuilder(); + var shell = await CreateShellAsync((shell) => + { + shell.Items.Add(new ContentPage() { Content = new Label() { Text = "Test Page" } }); + }); + + await CreateHandlerAndAddToWindow(shell, async (handler) => + { + await OpenFlyout(handler); + + var flyoutContent = handler.ViewController + .ChildViewControllers + .OfType() + .First(); + + var tableViewController = flyoutContent.ChildViewControllers + .OfType() + .FirstOrDefault(); + + Assert.NotNull(tableViewController); + Assert.False(tableViewController.TableView.ScrollsToTop, + "Shell flyout's table view should have ScrollsToTop disabled to avoid conflicting with content scroll views"); + }); + } + class ModalShellPage : ContentPage { public ModalShellPage() From f2c151e1fbdf43cd86533bc756246b587be9a8a1 Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Fri, 3 Apr 2026 11:53:31 +0200 Subject: [PATCH 3/5] Fix Shell test build error on MacCatalyst OpenFlyout is inside #if !MACCATALYST so it's unavailable on MacCatalyst. The test doesn't need to open the ShellTableViewController isflyout created in the constructor, so ScrollsToTop can be verified immediately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/DeviceTests/Elements/Shell/ShellTests.iOS.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.iOS.cs b/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.iOS.cs index 2cc5b7681676..e40cafcbc6ba 100644 --- a/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.iOS.cs +++ b/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.iOS.cs @@ -628,10 +628,8 @@ public async Task ShellFlyoutTableViewScrollsToTopIsDisabled() shell.Items.Add(new ContentPage() { Content = new Label() { Text = "Test Page" } }); }); - await CreateHandlerAndAddToWindow(shell, async (handler) => + await CreateHandlerAndAddToWindow(shell, (handler) => { - await OpenFlyout(handler); - var flyoutContent = handler.ViewController .ChildViewControllers .OfType() @@ -644,6 +642,8 @@ await CreateHandlerAndAddToWindow(shell, async (handler) => Assert.NotNull(tableViewController); Assert.False(tableViewController.TableView.ScrollsToTop, "Shell flyout's table view should have ScrollsToTop disabled to avoid conflicting with content scroll views"); + + return Task.CompletedTask; }); } From e87ce89b2dcc4ebdef25c1c49eb41c7c113eb2ae Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Fri, 3 Apr 2026 12:41:20 +0200 Subject: [PATCH 4/5] Add UI test verifying status bar tap scrolls CollectionView to top End-to-end test that creates a Shell with CollectionView, scrolls down, taps the iOS status bar area, and verifies the CollectionView scrolls back to top. This validates the full scroll-to-top behavior including the Shell flyout conflict fix. iOS-only since status bar tap to scroll is an iOS-specific feature. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../TestCases.HostApp/Issues/Issue19866.cs | 33 +++++++++++++++++ .../Tests/Issues/Issue19866.cs | 37 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/Controls/tests/TestCases.HostApp/Issues/Issue19866.cs create mode 100644 src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19866.cs diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue19866.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue19866.cs new file mode 100644 index 000000000000..1600df5208a2 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue19866.cs @@ -0,0 +1,33 @@ +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 19866, "CollectionView does not scroll to top on iOS status bar tap", PlatformAffected.iOS)] +public class Issue19866 : TestShell +{ + protected override void Init() + { + var items = Enumerable.Range(0, 100).Select(i => $"Item {i}").ToList(); + + var collectionView = new CollectionView + { + AutomationId = "TestCollectionView", + ItemsSource = items, + ItemTemplate = new DataTemplate(() => + { + var label = new Label + { + Margin = new Thickness(10, 20), + FontSize = 16 + }; + label.SetBinding(Label.TextProperty, new Binding(".")); + label.SetBinding(Label.AutomationIdProperty, new Binding(".")); + return label; + }) + }; + + AddContentPage(new ContentPage + { + Title = "Issue 19866", + Content = collectionView + }); + } +} diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19866.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19866.cs new file mode 100644 index 000000000000..631769b10c8d --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19866.cs @@ -0,0 +1,37 @@ +#if IOS +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue19866 : _IssuesUITest +{ + public Issue19866(TestDevice testDevice) : base(testDevice) { } + + public override string Issue => "CollectionView does not scroll to top on iOS status bar tap"; + + [Test] + [Category(UITestCategories.CollectionView)] + public void StatusBarTapScrollsCollectionViewToTop() + { + // Verify first item is visible initially + App.WaitForElement("Item 0"); + + // Scroll down multiple times to move well past the first item + App.ScrollDown("TestCollectionView", ScrollStrategy.Gesture); + App.ScrollDown("TestCollectionView", ScrollStrategy.Gesture); + App.ScrollDown("TestCollectionView", ScrollStrategy.Gesture); + + // First item should no longer be visible after scrolling + App.WaitForNoElement("Item 0"); + + // Tap the status bar area to trigger iOS scroll-to-top + var rect = App.WaitForElement("TestCollectionView").GetRect(); + App.TapCoordinates(rect.X + (rect.Width / 2), 5); + + // Verify first item is visible again after scroll-to-top + App.WaitForElement("Item 0", timeout: TimeSpan.FromSeconds(5)); + } +} +#endif From 262815b061f1d9d7b638e62dfe1d28efaa5def45 Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Wed, 8 Apr 2026 15:44:41 +0200 Subject: [PATCH 5/5] Fix CollectionViewScrollsToTopIsEnabled test to register CollectionViewHandler2 SetupBuilder() registers the legacy CollectionViewHandler, but the test expects CollectionViewHandler2. Use EnsureHandlerCreated with explicit CollectionViewHandler2 registration, matching the pattern used by the existing CollectionViewItemsArrangeCorrectly test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Elements/CollectionView/CollectionViewTests.iOS.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs b/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs index 9a787065e05c..4486c4075a56 100644 --- a/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs +++ b/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs @@ -313,7 +313,14 @@ public object LoadDataTemplate() [Fact] public async Task CollectionViewScrollsToTopIsEnabled() { - SetupBuilder(); + EnsureHandlerCreated(builder => + { + builder.ConfigureMauiHandlers(handlers => + { + handlers.AddHandler(); + handlers.AddHandler(); + }); + }); var collectionView = new CollectionView {