From 0f44d30e419c54d48015ff466634c0232ea9727c Mon Sep 17 00:00:00 2001 From: praveenkumarkarunanithi Date: Fri, 6 Mar 2026 17:41:09 +0530 Subject: [PATCH 1/5] test updated --- .../TestCases.HostApp/Issues/Issue34165.cs | 43 +++++++++++++++++++ .../Tests/Issues/Issue34165.cs | 40 +++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs create mode 100644 src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs new file mode 100644 index 000000000000..9c531e2f7714 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs @@ -0,0 +1,43 @@ +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 34165, "CollectionView is scrolling left/right when the collection is empty and inside a RefreshView", PlatformAffected.iOS)] +public class Issue34165 : ContentPage +{ + public const string CollectionViewId = "CollectionView"; + public const string EmptyViewLabelId = "EmptyViewLabel"; + public const string RefreshViewId = "RefreshView"; + + public Issue34165() + { + // The EmptyView label is the element we track — if horizontal scrolling occurs, + // its on-screen X position will change (the native scroll container moves it). + var emptyViewLabel = new Label + { + Text = "No items — swipe left/right here to test", + AutomationId = EmptyViewLabelId, + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center, + }; + + var collectionView = new CollectionView + { + AutomationId = CollectionViewId, + EmptyView = emptyViewLabel, + }; + + var refreshView = new RefreshView + { + AutomationId = RefreshViewId, + Content = collectionView, + }; + + refreshView.Refreshing += (s, e) => + { + ((RefreshView)s!).IsRefreshing = false; + }; + + Content = refreshView; + + Console.WriteLine("ISSUE34165: Page loaded — CollectionView is empty, horizontal scroll should NOT be possible"); + } +} diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs new file mode 100644 index 000000000000..ced05f166c71 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs @@ -0,0 +1,40 @@ +#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_CATALYST // Issue only affects iOS +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +[Category(UITestCategories.RefreshView)] +public class Issue34165 : _IssuesUITest +{ + public Issue34165(TestDevice testDevice) : base(testDevice) { } + + public override string Issue => "CollectionView is scrolling left/right when the collection is empty and inside a RefreshView"; + + [Test] + // Regression: empty CollectionView inside RefreshView should not allow horizontal scrolling on iOS. + // Detection strategy: measure the screen X position of the EmptyView label before and after + // a horizontal swipe — if the X coordinate shifts, the native scroll container moved (the bug). + public void EmptyCollectionViewInsideRefreshViewShouldNotScrollHorizontally() + { + App.WaitForElement("CollectionView"); + + // Record the EmptyView label's initial on-screen position + var rectBefore = App.WaitForElement("EmptyViewLabel").GetRect(); + + // Swipe right inside the CollectionView — this is the gesture that triggers the bug + App.ScrollRight("CollectionView", ScrollStrategy.Gesture, swipePercentage: 0.8, swipeSpeed: 300); + + // Small wait for any scroll momentum to settle + Thread.Sleep(500); + + // Re-measure — X must not have changed (no horizontal scroll should have occurred) + var rectAfter = App.WaitForElement("EmptyViewLabel").GetRect(); + + Assert.That(rectAfter.X, Is.EqualTo(rectBefore.X), + $"EmptyViewLabel X position changed from {rectBefore.X} to {rectAfter.X}. " + + "CollectionView must NOT scroll horizontally when empty inside a RefreshView."); + } +} +#endif From efd0d02d5177cf7348811992f934b7eb264a1bbd Mon Sep 17 00:00:00 2001 From: praveenkumarkarunanithi Date: Mon, 9 Mar 2026 12:55:21 +0530 Subject: [PATCH 2/5] Update LayoutFactory2.cs --- .../Handlers/Items2/iOS/LayoutFactory2.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs index a61b16aa851f..7b29e0fd7a68 100644 --- a/src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs +++ b/src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs @@ -457,6 +457,25 @@ public CustomUICollectionViewCompositionalLayout(LayoutSnapInfo snapInfo, UIColl _itemsUpdatingScrollMode = itemsUpdatingScrollMode; } + // UICollectionViewCompositionalLayout can report a content width larger than the viewport + // after a bounds change (e.g. when inside a RefreshView). This causes UIScrollView to + // enable horizontal scrolling on a vertical layout. Clamp the width to the actual bounds. (#34165) + public override CGSize CollectionViewContentSize + { + get + { + var size = base.CollectionViewContentSize; + if (Configuration.ScrollDirection == UICollectionViewScrollDirection.Vertical + && CollectionView is not null + && size.Width > CollectionView.Bounds.Width + && CollectionView.Bounds.Width > 0) + { + return new CGSize(CollectionView.Bounds.Width, size.Height); + } + return size; + } + } + public override void FinalizeCollectionViewUpdates() { base.FinalizeCollectionViewUpdates(); From c44aa47ccddae9a6a58cdc9041f0fd467184a225 Mon Sep 17 00:00:00 2001 From: praveenkumarkarunanithi Date: Mon, 9 Mar 2026 13:11:35 +0530 Subject: [PATCH 3/5] updated test --- .../tests/TestCases.HostApp/Issues/Issue34165.cs | 4 ---- .../TestCases.Shared.Tests/Tests/Issues/Issue34165.cs | 9 --------- 2 files changed, 13 deletions(-) diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs index 9c531e2f7714..ee5ee915df61 100644 --- a/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs @@ -9,8 +9,6 @@ public class Issue34165 : ContentPage public Issue34165() { - // The EmptyView label is the element we track — if horizontal scrolling occurs, - // its on-screen X position will change (the native scroll container moves it). var emptyViewLabel = new Label { Text = "No items — swipe left/right here to test", @@ -37,7 +35,5 @@ public Issue34165() }; Content = refreshView; - - Console.WriteLine("ISSUE34165: Page loaded — CollectionView is empty, horizontal scroll should NOT be possible"); } } diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs index ced05f166c71..1d8256217bd3 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs @@ -1,4 +1,3 @@ -#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_CATALYST // Issue only affects iOS using NUnit.Framework; using UITest.Appium; using UITest.Core; @@ -13,23 +12,16 @@ public Issue34165(TestDevice testDevice) : base(testDevice) { } public override string Issue => "CollectionView is scrolling left/right when the collection is empty and inside a RefreshView"; [Test] - // Regression: empty CollectionView inside RefreshView should not allow horizontal scrolling on iOS. - // Detection strategy: measure the screen X position of the EmptyView label before and after - // a horizontal swipe — if the X coordinate shifts, the native scroll container moved (the bug). public void EmptyCollectionViewInsideRefreshViewShouldNotScrollHorizontally() { App.WaitForElement("CollectionView"); - // Record the EmptyView label's initial on-screen position var rectBefore = App.WaitForElement("EmptyViewLabel").GetRect(); - // Swipe right inside the CollectionView — this is the gesture that triggers the bug App.ScrollRight("CollectionView", ScrollStrategy.Gesture, swipePercentage: 0.8, swipeSpeed: 300); - // Small wait for any scroll momentum to settle Thread.Sleep(500); - // Re-measure — X must not have changed (no horizontal scroll should have occurred) var rectAfter = App.WaitForElement("EmptyViewLabel").GetRect(); Assert.That(rectAfter.X, Is.EqualTo(rectBefore.X), @@ -37,4 +29,3 @@ public void EmptyCollectionViewInsideRefreshViewShouldNotScrollHorizontally() "CollectionView must NOT scroll horizontally when empty inside a RefreshView."); } } -#endif From 508a9d7e704ddb120dbad1b5e0b00a7acdef54b9 Mon Sep 17 00:00:00 2001 From: praveenkumarkarunanithi Date: Mon, 9 Mar 2026 17:24:07 +0530 Subject: [PATCH 4/5] Update Issue34165.cs --- .../tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs index 1d8256217bd3..fa9e52765dd5 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs @@ -20,8 +20,6 @@ public void EmptyCollectionViewInsideRefreshViewShouldNotScrollHorizontally() App.ScrollRight("CollectionView", ScrollStrategy.Gesture, swipePercentage: 0.8, swipeSpeed: 300); - Thread.Sleep(500); - var rectAfter = App.WaitForElement("EmptyViewLabel").GetRect(); Assert.That(rectAfter.X, Is.EqualTo(rectBefore.X), From 0aaa62c11575deaa3f7a500548a355314fb48eab Mon Sep 17 00:00:00 2001 From: praveenkumarkarunanithi Date: Wed, 11 Mar 2026 13:44:33 +0530 Subject: [PATCH 5/5] updated test as per AI concerns. --- src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs | 2 +- .../tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs index ee5ee915df61..4245292fdda1 100644 --- a/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs @@ -1,6 +1,6 @@ namespace Maui.Controls.Sample.Issues; -[Issue(IssueTracker.Github, 34165, "CollectionView is scrolling left/right when the collection is empty and inside a RefreshView", PlatformAffected.iOS)] +[Issue(IssueTracker.Github, 34165, "CollectionView is scrolling left/right when the collection is empty and inside a RefreshView", PlatformAffected.iOS | PlatformAffected.macOS)] public class Issue34165 : ContentPage { public const string CollectionViewId = "CollectionView"; diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs index fa9e52765dd5..3a2a8227dae8 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs @@ -1,3 +1,4 @@ +#if ANDROID || IOS // ScrollRight with ScrollStrategy.Gesture is not supported on Windows and MacCatalyst using NUnit.Framework; using UITest.Appium; using UITest.Core; @@ -22,8 +23,9 @@ public void EmptyCollectionViewInsideRefreshViewShouldNotScrollHorizontally() var rectAfter = App.WaitForElement("EmptyViewLabel").GetRect(); - Assert.That(rectAfter.X, Is.EqualTo(rectBefore.X), + Assert.That(rectAfter.X, Is.EqualTo(rectBefore.X).Within(1), $"EmptyViewLabel X position changed from {rectBefore.X} to {rectAfter.X}. " + "CollectionView must NOT scroll horizontally when empty inside a RefreshView."); } } +#endif