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(); 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..4245292fdda1 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs @@ -0,0 +1,39 @@ +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 | PlatformAffected.macOS)] +public class Issue34165 : ContentPage +{ + public const string CollectionViewId = "CollectionView"; + public const string EmptyViewLabelId = "EmptyViewLabel"; + public const string RefreshViewId = "RefreshView"; + + public Issue34165() + { + 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; + } +} 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..3a2a8227dae8 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs @@ -0,0 +1,31 @@ +#if ANDROID || IOS // ScrollRight with ScrollStrategy.Gesture is not supported on Windows and MacCatalyst +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] + public void EmptyCollectionViewInsideRefreshViewShouldNotScrollHorizontally() + { + App.WaitForElement("CollectionView"); + + var rectBefore = App.WaitForElement("EmptyViewLabel").GetRect(); + + App.ScrollRight("CollectionView", ScrollStrategy.Gesture, swipePercentage: 0.8, swipeSpeed: 300); + + var rectAfter = App.WaitForElement("EmptyViewLabel").GetRect(); + + 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