Skip to content

Commit 025e9ad

Browse files
committed
Add support for resizing header and footer
1 parent 10e4731 commit 025e9ad

File tree

8 files changed

+86
-50
lines changed

8 files changed

+86
-50
lines changed

src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,13 @@ public override void ViewWillAppear(bool animated)
199199
public override void ViewWillLayoutSubviews()
200200
{
201201
ConstrainItemsToBounds();
202-
InvalidateLayoutIfItemsMeasureChanged();
202+
203+
if (CollectionView is Items.MauiCollectionView { NeedsCellLayout: true } collectionView)
204+
{
205+
InvalidateLayoutIfItemsMeasureChanged();
206+
collectionView.NeedsCellLayout = false;
207+
}
208+
203209
base.ViewWillLayoutSubviews();
204210
InvalidateMeasureIfContentSizeChanged();
205211
LayoutEmptyView();

src/Controls/src/Core/Handlers/Items/iOS/MauiCollectionView.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ internal class MauiCollectionView : UICollectionView, IUIViewLifeCycleEvents, IP
1010
bool _invalidateParentWhenMovedToWindow;
1111

1212
WeakReference<ICustomMauiCollectionViewDelegate>? _customDelegate;
13+
14+
internal bool NeedsCellLayout { get; set; }
15+
1316
public MauiCollectionView(CGRect frame, UICollectionViewLayout layout) : base(frame, layout)
1417
{
1518
}
@@ -27,10 +30,12 @@ void IPlatformMeasureInvalidationController.InvalidateAncestorsMeasuresWhenMoved
2730

2831
void IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating)
2932
{
30-
if (!isPropagating)
33+
if (isPropagating)
3134
{
32-
SetNeedsLayout();
35+
NeedsCellLayout = true;
3336
}
37+
38+
SetNeedsLayout();
3439
}
3540

3641
[UnconditionalSuppressMessage("Memory", "MEM0002", Justification = IUIViewLifeCycleEvents.UnconditionalSuppressMessage)]

src/Controls/src/Core/Handlers/Items/iOS/StructuredItemsViewController.cs

Lines changed: 16 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,6 @@ internal override void Disconnect()
3131
{
3232
base.Disconnect();
3333

34-
if (_headerViewFormsElement is not null)
35-
{
36-
_headerViewFormsElement.MeasureInvalidated -= OnFormsElementMeasureInvalidated;
37-
}
38-
39-
if (_footerViewFormsElement is not null)
40-
{
41-
_footerViewFormsElement.MeasureInvalidated -= OnFormsElementMeasureInvalidated;
42-
}
43-
4434
_headerUIView = null;
4535
_headerViewFormsElement = null;
4636
_footerUIView = null;
@@ -86,27 +76,23 @@ protected override CGRect DetermineEmptyViewFrame()
8676

8777
public override void ViewWillLayoutSubviews()
8878
{
89-
base.ViewWillLayoutSubviews();
90-
91-
// This update is only relevant if you have a footer view because it's used to place the footer view
92-
// based on the ContentSize so we just update the positions if the ContentSize has changed
93-
if (_footerUIView != null)
79+
var hasHeaderOrFooter = _footerViewFormsElement is not null || _headerViewFormsElement is not null;
80+
if (hasHeaderOrFooter && CollectionView is MauiCollectionView { NeedsCellLayout: true } collectionView)
9481
{
95-
var emptyView = CollectionView.ViewWithTag(EmptyTag);
96-
97-
if (IsHorizontal)
82+
if (_headerViewFormsElement is not null)
9883
{
99-
if (_footerUIView.Frame.X != ItemsViewLayout.CollectionViewContentSize.Width ||
100-
_footerUIView.Frame.X < emptyView?.Frame.X)
101-
UpdateHeaderFooterPosition();
84+
RemeasureLayout(_headerViewFormsElement);
10285
}
103-
else
86+
87+
if (_footerViewFormsElement is not null)
10488
{
105-
if (_footerUIView.Frame.Y != ItemsViewLayout.CollectionViewContentSize.Height ||
106-
_footerUIView.Frame.Y < (emptyView?.Frame.Y + emptyView?.Frame.Height))
107-
UpdateHeaderFooterPosition();
89+
RemeasureLayout(_footerViewFormsElement);
10890
}
91+
92+
UpdateHeaderFooterPosition();
10993
}
94+
95+
base.ViewWillLayoutSubviews();
11096
}
11197

11298
internal void UpdateFooterView()
@@ -123,15 +109,13 @@ internal void UpdateHeaderView()
123109
UpdateHeaderFooterPosition();
124110
}
125111

126-
127112
internal void UpdateSubview(object view, DataTemplate viewTemplate, nint viewTag, ref UIView uiView, ref VisualElement formsElement)
128113
{
129114
uiView?.RemoveFromSuperview();
130115

131116
if (formsElement != null)
132117
{
133118
ItemsView.RemoveLogicalChild(formsElement);
134-
formsElement.MeasureInvalidated -= OnFormsElementMeasureInvalidated;
135119
}
136120

137121
UpdateView(view, viewTemplate, ref uiView, ref formsElement);
@@ -150,7 +134,6 @@ internal void UpdateSubview(object view, DataTemplate viewTemplate, nint viewTag
150134
if (formsElement != null)
151135
{
152136
RemeasureLayout(formsElement);
153-
formsElement.MeasureInvalidated += OnFormsElementMeasureInvalidated;
154137
}
155138
else
156139
{
@@ -176,7 +159,11 @@ void UpdateHeaderFooterPosition()
176159
}
177160

178161
if (_footerUIView != null && (_footerUIView.Frame.X != ItemsViewLayout.CollectionViewContentSize.Width || emptyWidth > 0))
179-
_footerUIView.Frame = new CoreGraphics.CGRect(ItemsViewLayout.CollectionViewContentSize.Width + emptyWidth, 0, footerWidth, CollectionView.Frame.Height);
162+
{
163+
_footerUIView.Frame = new CoreGraphics.CGRect(
164+
ItemsViewLayout.CollectionViewContentSize.Width + emptyWidth, 0, footerWidth,
165+
CollectionView.Frame.Height);
166+
}
180167

181168
if (CollectionView.ContentInset.Left != headerWidth || CollectionView.ContentInset.Right != footerWidth)
182169
{
@@ -249,14 +236,5 @@ protected override void HandleFormsElementMeasureInvalidated(VisualElement forms
249236
var size = base.GetSize();
250237
return new Size(size.Value.Width, size.Value.Height + (_headerUIView?.Frame.Height ?? 0) + (_footerUIView?.Frame.Height ?? 0));
251238
}
252-
253-
internal void UpdateLayoutMeasurements()
254-
{
255-
if (_headerViewFormsElement != null)
256-
HandleFormsElementMeasureInvalidated(_headerViewFormsElement);
257-
258-
if (_footerViewFormsElement != null)
259-
HandleFormsElementMeasureInvalidated(_footerViewFormsElement);
260-
}
261239
}
262240
}

src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,6 @@ void IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating
288288
if (!_measureInvalidated && _bound)
289289
{
290290
_measureInvalidated = true;
291-
Superview?.SetNeedsLayout();
292291
}
293292
}
294293

src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,15 +189,21 @@ public override void LoadView()
189189

190190
public override void ViewWillLayoutSubviews()
191191
{
192-
InvalidateLayoutIfItemsMeasureChanged();
192+
if (CollectionView is Items.MauiCollectionView { NeedsCellLayout: true } collectionView)
193+
{
194+
InvalidateLayoutIfItemsMeasureChanged();
195+
collectionView.NeedsCellLayout = false;
196+
}
197+
193198
base.ViewWillLayoutSubviews();
194199
LayoutEmptyView();
195200
InvalidateMeasureIfContentSizeChanged();
196201
}
197202

198203
void InvalidateLayoutIfItemsMeasureChanged()
199204
{
200-
var visibleCells = CollectionView.VisibleCells;
205+
var collectionView = CollectionView;
206+
var visibleCells = collectionView.VisibleCells;
201207
List<NSIndexPath> invalidatedPaths = null;
202208

203209
var visibleCellsLength = visibleCells.Length;
@@ -206,7 +212,7 @@ void InvalidateLayoutIfItemsMeasureChanged()
206212
if (visibleCells[n] is TemplatedCell2 { MeasureInvalidated: true } cell)
207213
{
208214
invalidatedPaths ??= new List<NSIndexPath>(visibleCellsLength);
209-
var path = CollectionView.IndexPathForCell(cell);
215+
var path = collectionView.IndexPathForCell(cell);
210216
invalidatedPaths.Add(path);
211217
}
212218
}
@@ -215,7 +221,7 @@ void InvalidateLayoutIfItemsMeasureChanged()
215221
{
216222
var layoutInvalidationContext = new UICollectionViewLayoutInvalidationContext();
217223
layoutInvalidationContext.InvalidateItems(invalidatedPaths.ToArray());
218-
CollectionView.CollectionViewLayout.InvalidateLayout(layoutInvalidationContext);
224+
collectionView.CollectionViewLayout.InvalidateLayout(layoutInvalidationContext);
219225
}
220226
}
221227

src/Controls/src/Core/Handlers/Items2/iOS/StructuredItemsViewController2.cs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#nullable disable
22
using System;
3+
using System.Collections.Generic;
34
using CoreGraphics;
45
using Foundation;
5-
using Microsoft.Maui.Controls.Handlers.Items;
66
using ObjCRuntime;
77
using UIKit;
88

@@ -181,7 +181,52 @@ protected override CGRect DetermineEmptyViewFrame()
181181

182182
public override void ViewWillLayoutSubviews()
183183
{
184+
if (CollectionView is Items.MauiCollectionView { NeedsCellLayout: true })
185+
{
186+
InvalidateLayoutIfItemsMeasureChanged();
187+
}
188+
184189
base.ViewWillLayoutSubviews();
185190
}
191+
192+
void InvalidateLayoutIfItemsMeasureChanged()
193+
{
194+
var collectionView = CollectionView;
195+
List<NSIndexPath> invalidatedPaths = null;
196+
197+
if (ItemsView.Header is not null || ItemsView.HeaderTemplate is not null)
198+
{
199+
var visibleHeaders = collectionView.GetVisibleSupplementaryViews(UICollectionElementKindSectionKey.Header);
200+
foreach (var header in visibleHeaders)
201+
{
202+
if (header is TemplatedCell2 { MeasureInvalidated: true } headerCell)
203+
{
204+
invalidatedPaths ??= new List<NSIndexPath>(2);
205+
invalidatedPaths.Add(collectionView.IndexPathForCell(headerCell));
206+
}
207+
}
208+
}
209+
210+
211+
if (ItemsView.Footer is not null || ItemsView.FooterTemplate is not null)
212+
{
213+
var visibleFooters = collectionView.GetVisibleSupplementaryViews(UICollectionElementKindSectionKey.Footer);
214+
foreach (var footer in visibleFooters)
215+
{
216+
if (footer is TemplatedCell2 { MeasureInvalidated: true } footerCell)
217+
{
218+
invalidatedPaths ??= new List<NSIndexPath>(1);
219+
invalidatedPaths.Add(collectionView.IndexPathForCell(footerCell));
220+
}
221+
}
222+
}
223+
224+
if (invalidatedPaths != null)
225+
{
226+
var layoutInvalidationContext = new UICollectionViewLayoutInvalidationContext();
227+
layoutInvalidationContext.InvalidateItems(invalidatedPaths.ToArray());
228+
collectionView.CollectionViewLayout.InvalidateLayout(layoutInvalidationContext);
229+
}
230+
}
186231
}
187232
}

src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,6 @@ void IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating
283283
if (!_measureInvalidated && _bound)
284284
{
285285
_measureInvalidated = true;
286-
Superview?.SetNeedsLayout();
287286
}
288287
}
289288
}

src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25362.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ public Issue25362(TestDevice device) : base(device)
1313

1414
[Test]
1515
[Category(UITestCategories.CollectionView)]
16-
[FailsOnIOSWhenRunningOnXamarinUITest("This is not working for CV2 yet")]
17-
[FailsOnMacWhenRunningOnXamarinUITest("This is not working for CV2 yet")]
1816
public void HeaderShouldNotCollapseWithItems()
1917
{
2018
App.WaitForElement("button");

0 commit comments

Comments
 (0)