diff --git a/src/Controls/src/Core/Handlers/Items/iOS/CarouselTemplatedCell.cs b/src/Controls/src/Core/Handlers/Items/iOS/CarouselTemplatedCell.cs index ceec196815de..01339d4bf446 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/CarouselTemplatedCell.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/CarouselTemplatedCell.cs @@ -32,7 +32,10 @@ public override void ConstrainTo(CGSize constraint) public override CGSize Measure() { - return new CGSize(_constraint.Width, _constraint.Height); + // Go through the measure pass even if the constraints are fixed + // to ensure arrange pass has the appropriate desired size in place. + PlatformHandler.VirtualView.Measure(_constraint.Width, _constraint.Height); + return _constraint; } protected override (bool, Size) NeedsContentSizeUpdate(Size currentSize) @@ -42,7 +45,7 @@ protected override (bool, Size) NeedsContentSizeUpdate(Size currentSize) protected override bool AttributesConsistentWithConstrainedDimension(UICollectionViewLayoutAttributes attributes) { - return false; + return _constraint.IsCloseTo(attributes.Frame.Size); } } } diff --git a/src/Controls/src/Core/Handlers/Items/iOS/CarouselViewController.cs b/src/Controls/src/Core/Handlers/Items/iOS/CarouselViewController.cs index 8e632568f3b0..a1de8b9367cb 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/CarouselViewController.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/CarouselViewController.cs @@ -60,7 +60,6 @@ public override UICollectionViewCell GetCell(UICollectionView collectionView, NS if (cell is TemplatedCell templatedCell) { - templatedCell.IsCarouselViewCell = true; UpdateTemplatedCell(templatedCell, correctedIndexPath); } } @@ -186,6 +185,9 @@ protected override string DetermineCellReuseId(NSIndexPath indexPath) return base.DetermineCellReuseId(NSIndexPath.FromItemSection(itemIndex, 0)); } + private protected override (Type CellType, string CellTypeReuseId) DetermineTemplatedCellType() + => (typeof(CarouselTemplatedCell), "maui_carousel"); + protected override void RegisterViewTypes() { CollectionView.RegisterClassForCell(typeof(CarouselTemplatedCell), CarouselTemplatedCell.ReuseId); diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs index 17494ee38c64..06ffe3f7bd58 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs @@ -503,9 +503,9 @@ protected virtual string DetermineCellReuseId(NSIndexPath indexPath) var dataTemplate = ItemsView.ItemTemplate.SelectDataTemplate(item, ItemsView); var cellOrientation = ItemsViewLayout.ScrollDirection == UICollectionViewScrollDirection.Vertical ? "v" : "h"; - var cellType = ItemsViewLayout.ScrollDirection == UICollectionViewScrollDirection.Vertical ? typeof(VerticalCell) : typeof(HorizontalCell); + (Type cellType, var cellTypeReuseId) = DetermineTemplatedCellType(); - var reuseId = $"_maui_{cellOrientation}_{dataTemplate.Id}"; + var reuseId = $"_{cellTypeReuseId}_{cellOrientation}_{dataTemplate.Id}"; if (!_cellReuseIds.Contains(reuseId)) { @@ -521,6 +521,11 @@ protected virtual string DetermineCellReuseId(NSIndexPath indexPath) : VerticalDefaultCell.ReuseId; } + private protected virtual (Type CellType, string CellTypeReuseId) DetermineTemplatedCellType() + { + return (ItemsViewLayout.ScrollDirection == UICollectionViewScrollDirection.Vertical ? typeof(VerticalCell) : typeof(HorizontalCell), "maui"); + } + [Obsolete("Use DetermineCellReuseId(NSIndexPath indexPath) instead.")] protected virtual string DetermineCellReuseId() { diff --git a/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs b/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs index 0685557306cb..d3c238ec10e4 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs @@ -26,8 +26,6 @@ public event EventHandler LayoutAttributesChan remove => _weakEventManager.RemoveEventHandler(value); } - internal bool IsCarouselViewCell; - protected CGSize ConstrainedSize; protected nfloat ConstrainedDimension; @@ -106,13 +104,6 @@ public override UICollectionViewLayoutAttributes PreferredLayoutAttributesFittin var size = ConstrainedSize == default ? Measure() : ConstrainedSize; _size = size.ToSize(); _needsArrange = true; - - if (IsCarouselViewCell && PlatformHandler?.VirtualView is { } virtualView) - { - var constraints = new Size(preferredAttributes.Size.Width, double.PositiveInfinity); - _size = virtualView.Measure(constraints.Width, constraints.Height); - } - _measureInvalidated = false; preferredAttributes.Frame = new CGRect(preferredAttributes.Frame.Location, _size); // Ensure we get a layout pass to arrange the virtual view. diff --git a/src/Controls/src/Core/Handlers/Items2/CarouselViewHandler2.iOS.cs b/src/Controls/src/Core/Handlers/Items2/CarouselViewHandler2.iOS.cs index 83e357b87faa..dfb0270aaae1 100644 --- a/src/Controls/src/Core/Handlers/Items2/CarouselViewHandler2.iOS.cs +++ b/src/Controls/src/Core/Handlers/Items2/CarouselViewHandler2.iOS.cs @@ -38,8 +38,8 @@ protected override CarouselViewController2 CreateController(CarouselView newElem protected override UICollectionViewLayout SelectLayout() { - bool IsHorizontal = VirtualView.ItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal; - UICollectionViewScrollDirection scrollDirection = IsHorizontal ? UICollectionViewScrollDirection.Horizontal : UICollectionViewScrollDirection.Vertical; + bool isHorizontal = VirtualView.ItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal; + UICollectionViewScrollDirection scrollDirection = isHorizontal ? UICollectionViewScrollDirection.Horizontal : UICollectionViewScrollDirection.Vertical; NSCollectionLayoutDimension itemWidth = NSCollectionLayoutDimension.CreateFractionalWidth(1); NSCollectionLayoutDimension itemHeight = NSCollectionLayoutDimension.CreateFractionalHeight(1); @@ -47,6 +47,11 @@ protected override UICollectionViewLayout SelectLayout() NSCollectionLayoutDimension groupHeight = NSCollectionLayoutDimension.CreateFractionalHeight(1); nfloat itemSpacing = 0; NSCollectionLayoutGroup group = null; + + var layoutConfiguration = new UICollectionViewCompositionalLayoutConfiguration + { + ScrollDirection = scrollDirection + }; var layout = new UICollectionViewCompositionalLayout((sectionIndex, environment) => { @@ -55,7 +60,7 @@ protected override UICollectionViewLayout SelectLayout() return null; } double sectionMargin = 0.0; - if (!IsHorizontal) + if (!isHorizontal) { sectionMargin = VirtualView.PeekAreaInsets.VerticalThickness / 2; var newGroupHeight = environment.Container.ContentSize.Height - VirtualView.PeekAreaInsets.VerticalThickness; @@ -81,19 +86,19 @@ protected override UICollectionViewLayout SelectLayout() if (OperatingSystem.IsIOSVersionAtLeast(16)) { - group = IsHorizontal ? NSCollectionLayoutGroup.GetHorizontalGroup(groupSize, item, 1) : + group = isHorizontal ? NSCollectionLayoutGroup.GetHorizontalGroup(groupSize, item, 1) : NSCollectionLayoutGroup.GetVerticalGroup(groupSize, item, 1); } else { - group = IsHorizontal ? NSCollectionLayoutGroup.CreateHorizontal(groupSize, item, 1) : + group = isHorizontal ? NSCollectionLayoutGroup.CreateHorizontal(groupSize, item, 1) : NSCollectionLayoutGroup.CreateVertical(groupSize, item, 1); } // Create our section layout var section = NSCollectionLayoutSection.Create(group: group); section.InterGroupSpacing = itemSpacing; - section.OrthogonalScrollingBehavior = IsHorizontal ? UICollectionLayoutSectionOrthogonalScrollingBehavior.GroupPagingCentered : UICollectionLayoutSectionOrthogonalScrollingBehavior.None; + section.OrthogonalScrollingBehavior = isHorizontal ? UICollectionLayoutSectionOrthogonalScrollingBehavior.GroupPagingCentered : UICollectionLayoutSectionOrthogonalScrollingBehavior.None; section.VisibleItemsInvalidationHandler = (items, offset, env) => { //This will allow us to SetPosition when we are scrolling the items @@ -152,7 +157,7 @@ protected override UICollectionViewLayout SelectLayout() }; return section; - }); + }, layoutConfiguration); return layout; } diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/CarouselTemplatedCell2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/CarouselTemplatedCell2.cs new file mode 100644 index 000000000000..f24d5ebc9c94 --- /dev/null +++ b/src/Controls/src/Core/Handlers/Items2/iOS/CarouselTemplatedCell2.cs @@ -0,0 +1,22 @@ +#nullable disable +using CoreGraphics; +using Foundation; +using Microsoft.Maui.Graphics; +using UIKit; + +namespace Microsoft.Maui.Controls.Handlers.Items2 +{ + internal sealed class CarouselTemplatedCell2 : TemplatedCell2 + { + internal new const string ReuseId = "Microsoft.Maui.Controls.CarouselTemplatedCell2"; + + [Export("initWithFrame:")] + [Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)] + public CarouselTemplatedCell2(CGRect frame) : base(frame) + { + } + + private protected override Size GetMeasureConstraints(UICollectionViewLayoutAttributes preferredAttributes) + => preferredAttributes.Size.ToSize(); + } +} diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cs index c7b5d7f429e3..ece28de99c98 100644 --- a/src/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cs +++ b/src/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cs @@ -117,6 +117,9 @@ protected override string DetermineCellReuseId(NSIndexPath indexPath) return base.DetermineCellReuseId(itemIndex); } + private protected override (Type CellType, string CellTypeReuseId) DetermineTemplatedCellType() + => (typeof(CarouselTemplatedCell2), CarouselTemplatedCell2.ReuseId); + protected override Items.IItemsViewSource CreateItemsViewSource() { var itemsSource = ItemsSourceFactory2.CreateForCarouselView(ItemsView.ItemsSource, this, ItemsView.Loop); diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs index 7e93d347b36e..cc899c373f09 100644 --- a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs +++ b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs @@ -330,10 +330,9 @@ protected virtual string DetermineCellReuseId(NSIndexPath indexPath) var dataTemplate = ItemsView.ItemTemplate.SelectDataTemplate(item, ItemsView); - var cellType = typeof(TemplatedCell2); - var orientation = ScrollDirection == UICollectionViewScrollDirection.Horizontal ? "Horizontal" : "Vertical"; - var reuseId = $"{TemplatedCell2.ReuseId}.{orientation}.{dataTemplate.Id}"; + (Type cellType, var cellTypeReuseId) = DetermineTemplatedCellType(); + var reuseId = $"{cellTypeReuseId}.{orientation}.{dataTemplate.Id}"; if (!_cellReuseIds.Contains(reuseId)) { @@ -347,6 +346,9 @@ protected virtual string DetermineCellReuseId(NSIndexPath indexPath) return ScrollDirection == UICollectionViewScrollDirection.Horizontal ? HorizontalDefaultCell2.ReuseId : VerticalDefaultCell2.ReuseId; } + private protected virtual (Type CellType, string CellTypeReuseId) DetermineTemplatedCellType() + => (typeof(TemplatedCell2), TemplatedCell2.ReuseId); + protected virtual void RegisterViewTypes() { CollectionView.RegisterClassForCell(typeof(HorizontalDefaultCell2), HorizontalDefaultCell2.ReuseId); diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs index 16ba34065d9d..dc88693c6571 100644 --- a/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs +++ b/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs @@ -86,9 +86,7 @@ public override UICollectionViewLayoutAttributes PreferredLayoutAttributesFittin if (PlatformHandler?.VirtualView is { } virtualView) { - var constraints = ScrollDirection == UICollectionViewScrollDirection.Vertical - ? new Size(preferredAttributes.Size.Width, double.PositiveInfinity) - : new Size(double.PositiveInfinity, preferredAttributes.Size.Height); + var constraints = GetMeasureConstraints(preferredAttributes); if (_measureInvalidated || _cachedConstraints != constraints) { @@ -98,9 +96,12 @@ public override UICollectionViewLayoutAttributes PreferredLayoutAttributesFittin _needsArrange = true; } - var size = ScrollDirection == UICollectionViewScrollDirection.Vertical - ? new Size(preferredAttributes.Size.Width, _measuredSize.Height) - : new Size(_measuredSize.Width, preferredAttributes.Size.Height); + var preferredSize = preferredAttributes.Size; + // Use measured size only when unconstrained + var size = new Size( + double.IsPositiveInfinity(constraints.Width) ? _measuredSize.Width : preferredSize.Width, + double.IsPositiveInfinity(constraints.Height) ? _measuredSize.Height : preferredSize.Height + ); preferredAttributes.Frame = new CGRect(preferredAttributes.Frame.Location, size); preferredAttributes.ZIndex = 2; @@ -111,6 +112,14 @@ public override UICollectionViewLayoutAttributes PreferredLayoutAttributesFittin return preferredAttributes; } + private protected virtual Size GetMeasureConstraints(UICollectionViewLayoutAttributes preferredAttributes) + { + var constraints = ScrollDirection == UICollectionViewScrollDirection.Vertical + ? new Size(preferredAttributes.Size.Width, double.PositiveInfinity) + : new Size(double.PositiveInfinity, preferredAttributes.Size.Height); + return constraints; + } + public override void LayoutSubviews() { base.LayoutSubviews();