diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/GroupableItemsViewController2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/GroupableItemsViewController2.cs index 0c3be0ff8e71..ff0b7c98dda9 100644 --- a/src/Controls/src/Core/Handlers/Items2/iOS/GroupableItemsViewController2.cs +++ b/src/Controls/src/Core/Handlers/Items2/iOS/GroupableItemsViewController2.cs @@ -127,6 +127,8 @@ void UpdateTemplatedSupplementaryView(TemplatedCell2 cell, NSString elementKind, var bindingContext = ItemsSource.Group(indexPath); + // Mark this templated cell as a supplementary view (header/footer) + cell.isSupplementaryView = true; cell.isHeaderOrFooterChanged = true; cell.Bind(template, bindingContext, ItemsView); cell.isHeaderOrFooterChanged = false; diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs index f9af7cc33fab..a1c239ef4240 100644 --- a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs +++ b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs @@ -119,6 +119,8 @@ public override UICollectionViewCell GetCell(UICollectionView collectionView, NS { TemplatedCell2.ScrollDirection = ScrollDirection; + // Ensure this cell is treated as a regular item cell (not a supplementary view) + TemplatedCell2.isSupplementaryView = false; TemplatedCell2.Bind(ItemsView.ItemTemplate, ItemsSource[indexpathAdjusted], ItemsView); } else if (cell is DefaultCell2 DefaultCell2) diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/StructuredItemsViewController2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/StructuredItemsViewController2.cs index 87413f640e97..ec01d92cd6d0 100644 --- a/src/Controls/src/Core/Handlers/Items2/iOS/StructuredItemsViewController2.cs +++ b/src/Controls/src/Core/Handlers/Items2/iOS/StructuredItemsViewController2.cs @@ -130,6 +130,7 @@ void UpdateTemplatedSupplementaryView(TemplatedCell2 cell, NSString elementKind) { bool isHeader = elementKind == UICollectionElementKindSectionKey.Header; cell.isHeaderOrFooterChanged = true; + cell.isSupplementaryView = true; if (isHeader) { diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs index 7136d9e1c1b6..1e7f74e7a807 100644 --- a/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs +++ b/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs @@ -40,6 +40,8 @@ public event EventHandler LayoutAttributesCha Size _measuredSize; Size _cachedConstraints; + // Indicates the cell is being used as a supplementary view (group header/footer) + internal bool isSupplementaryView = false; internal bool MeasureInvalidated => _measureInvalidated; // Flags changes confined to the header/footer, preventing unnecessary recycling and revalidation of templated cells. @@ -107,20 +109,28 @@ public override UICollectionViewLayoutAttributes PreferredLayoutAttributesFittin if (_measureInvalidated || _cachedConstraints != constraints) { - // Check if we should use the cached first item size for MeasureFirstItem optimization - var cachedSize = GetCachedFirstItemSizeFromHandler(); - if (cachedSize != CGSize.Empty) + // Only use the cached first-item measurement for actual item cells (not headers/footers) + if (!isSupplementaryView) { - _measuredSize = cachedSize.ToSize(); - // Even when we have a cached measurement, we still need to call Measure - // to update the virtual view's internal state and bookkeeping - virtualView.Measure(constraints.Width, _measuredSize.Height); + var cachedSize = GetCachedFirstItemSizeFromHandler(); + if (cachedSize != CGSize.Empty) + { + _measuredSize = cachedSize.ToSize(); + // Even when we have a cached measurement, we still need to call Measure + // to update the virtual view's internal state and bookkeeping + virtualView.Measure(constraints.Width, _measuredSize.Height); + } + else + { + _measuredSize = virtualView.Measure(constraints.Width, constraints.Height); + // If this is the first item being measured, cache it for MeasureFirstItem strategy + SetCachedFirstItemSizeToHandler(_measuredSize.ToCGSize()); + } } else { + // For headers/footers, always measure directly without using or updating the first-item cache _measuredSize = virtualView.Measure(constraints.Width, constraints.Height); - // If this is the first item being measured, cache it for MeasureFirstItem strategy - SetCachedFirstItemSizeToHandler(_measuredSize.ToCGSize()); } _cachedConstraints = constraints; _needsArrange = true; @@ -194,6 +204,7 @@ public override void LayoutSubviews() public override void PrepareForReuse() { //Unbind(); + isSupplementaryView = false; base.PrepareForReuse(); } diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33130.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue33130.xaml new file mode 100644 index 000000000000..567d93a8ad5d --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33130.xaml @@ -0,0 +1,77 @@ + + + + +