diff --git a/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs b/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs index 39b8e8135b34..768764fd91b4 100644 --- a/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs +++ b/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs @@ -259,6 +259,7 @@ static WStyle GetItemContainerStyle(GridItemsLayout layout) style.Setters.Add(new WSetter(FrameworkElement.MarginProperty, margin)); style.Setters.Add(new WSetter(Control.PaddingProperty, WinUIHelpers.CreateThickness(0))); style.Setters.Add(new WSetter(Control.HorizontalContentAlignmentProperty, HorizontalAlignment.Stretch)); + style.Setters.Add(new WSetter(Control.VerticalContentAlignmentProperty, VerticalAlignment.Stretch)); return style; } diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs index dfec3353818e..38d63c2c4380 100644 --- a/src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs +++ b/src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs @@ -85,6 +85,35 @@ static NSCollectionLayoutBoundarySupplementaryItem[] CreateSupplementaryItems(La return []; } + static void ApplyHeaderFooterBoundarySpacing(NSCollectionLayoutSection section, UICollectionViewScrollDirection scrollDirection, double verticalSpacing, double horizontalSpacing, bool hasHeader, bool hasFooter, bool applySpacing) + { + if (!applySpacing) + { + return; + } + + if (scrollDirection == UICollectionViewScrollDirection.Vertical) + { + var topInset = hasHeader ? new NFloat(verticalSpacing) : new NFloat(0); + var bottomInset = hasFooter ? new NFloat(verticalSpacing) : new NFloat(0); + + if (topInset > 0 || bottomInset > 0) + { + section.ContentInsets = new NSDirectionalEdgeInsets(topInset, 0, bottomInset, 0); + } + } + else + { + var leadingInset = hasHeader ? new NFloat(horizontalSpacing) : new NFloat(0); + var trailingInset = hasFooter ? new NFloat(horizontalSpacing) : new NFloat(0); + + if (leadingInset > 0 || trailingInset > 0) + { + section.ContentInsets = new NSDirectionalEdgeInsets(0, leadingInset, 0, trailingInset); + } + } + } + static UICollectionViewLayout CreateListLayout(UICollectionViewScrollDirection scrollDirection, LayoutGroupingInfo groupingInfo, LayoutHeaderFooterInfo layoutHeaderFooterInfo, LayoutSnapInfo snapInfo, NSCollectionLayoutDimension itemWidth, NSCollectionLayoutDimension itemHeight, NSCollectionLayoutDimension groupWidth, NSCollectionLayoutDimension groupHeight, double itemSpacing, Func? peekAreaInsetsFunc, ItemsLayout itemsLayout) { var layoutConfiguration = new UICollectionViewCompositionalLayoutConfiguration(); @@ -145,31 +174,14 @@ static UICollectionViewLayout CreateListLayout(UICollectionViewScrollDirection s // For grouped sections with a group header/footer, add content insets to create // the gap between the header/footer supplementary item and the first/last item. // InterSectionSpacing (set on layoutConfiguration above) handles the gap between sections. - if (groupingInfo.IsGrouped && itemSpacing > 0) - { - if (scrollDirection == UICollectionViewScrollDirection.Horizontal) - { - var leadingInset = groupingInfo.HasHeader ? new NFloat(itemSpacing) : new NFloat(0); - var trailingInset = groupingInfo.HasFooter ? new NFloat(itemSpacing) : new NFloat(0); - - if (leadingInset > 0 || trailingInset > 0) - { - section.ContentInsets = new NSDirectionalEdgeInsets(0, leadingInset, 0, trailingInset); - } - } - else - { - // Vertical: top inset creates gap between header and first item, - // bottom inset creates gap between last item and footer. - var topInset = groupingInfo.HasHeader ? new NFloat(itemSpacing) : new NFloat(0); - var bottomInset = groupingInfo.HasFooter ? new NFloat(itemSpacing) : new NFloat(0); - - if (topInset > 0 || bottomInset > 0) - { - section.ContentInsets = new NSDirectionalEdgeInsets(topInset, 0, bottomInset, 0); - } - } - } + ApplyHeaderFooterBoundarySpacing( + section, + scrollDirection, + itemSpacing, + itemSpacing, + groupingInfo.HasHeader, + groupingInfo.HasFooter, + groupingInfo.IsGrouped && itemSpacing > 0); // Create header and footer for group section.BoundarySupplementaryItems = CreateSupplementaryItems( @@ -192,6 +204,15 @@ static UICollectionViewLayout CreateGridLayout(UICollectionViewScrollDirection s var layoutConfiguration = new UICollectionViewCompositionalLayoutConfiguration(); layoutConfiguration.ScrollDirection = scrollDirection; + var mainAxisSpacing = scrollDirection == UICollectionViewScrollDirection.Vertical + ? verticalItemSpacing + : horizontalItemSpacing; + + if (groupingInfo.IsGrouped && mainAxisSpacing > 0) + { + layoutConfiguration.InterSectionSpacing = new NFloat(mainAxisSpacing); + } + var layout = new CustomUICollectionViewCompositionalLayout(snapInfo, groupingInfo, headerFooterInfo, (sectionIndex, environment) => { // Each item has a size @@ -235,6 +256,14 @@ static UICollectionViewLayout CreateGridLayout(UICollectionViewScrollDirection s section.InterGroupSpacing = new NFloat(horizontalItemSpacing); } + ApplyHeaderFooterBoundarySpacing( + section, + scrollDirection, + verticalItemSpacing, + horizontalItemSpacing, + groupingInfo.HasHeader, + groupingInfo.HasFooter, + groupingInfo.IsGrouped && mainAxisSpacing > 0); section.BoundarySupplementaryItems = CreateSupplementaryItems( groupingInfo, diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/GroupedCollectionViewGridLayoutRendersCorrectly.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/GroupedCollectionViewGridLayoutRendersCorrectly.png index 06a76bc22132..6f83252d9146 100644 Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/GroupedCollectionViewGridLayoutRendersCorrectly.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/GroupedCollectionViewGridLayoutRendersCorrectly.png differ diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35700.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35700.cs index f6d49cbef26d..01125c5a1df9 100644 --- a/src/Controls/tests/TestCases.HostApp/Issues/Issue35700.cs +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35700.cs @@ -5,7 +5,7 @@ namespace Maui.Controls.Sample.Issues; [Issue(IssueTracker.Github, 35700, "Grouped CollectionView items not rendered properly on Android with GridItemsLayout", - PlatformAffected.Android)] + PlatformAffected.Android | PlatformAffected.iOS | PlatformAffected.macOS | PlatformAffected.UWP)] public class Issue35700 : TestContentPage { protected override void Init() diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/GroupedCollectionViewGridLayoutRendersCorrectly.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/GroupedCollectionViewGridLayoutRendersCorrectly.png new file mode 100644 index 000000000000..b68ba6d5d39c Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/GroupedCollectionViewGridLayoutRendersCorrectly.png differ diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GroupedCollectionViewGridLayoutRendersCorrectly.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GroupedCollectionViewGridLayoutRendersCorrectly.png new file mode 100644 index 000000000000..41940df288a4 Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GroupedCollectionViewGridLayoutRendersCorrectly.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/GroupedCollectionViewGridLayoutRendersCorrectly.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/GroupedCollectionViewGridLayoutRendersCorrectly.png new file mode 100644 index 000000000000..c51c2592d9d1 Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/GroupedCollectionViewGridLayoutRendersCorrectly.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GroupedCollectionViewGridLayoutRendersCorrectly.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GroupedCollectionViewGridLayoutRendersCorrectly.png new file mode 100644 index 000000000000..c51c2592d9d1 Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GroupedCollectionViewGridLayoutRendersCorrectly.png differ