Skip to content

Commit dd13512

Browse files
authored
[SR5] Fix footer resizing issue (#29064)
* Revert "[iOS] CollectionView footer sizing when source is empty in SR5 (#28971)" * - fix header/footer sizing issue * - add screenshots * - set to cv1 because behavior currently differs
1 parent d35fff9 commit dd13512

File tree

9 files changed

+129
-24
lines changed

9 files changed

+129
-24
lines changed

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,12 +199,6 @@ public override void ViewWillAppear(bool animated)
199199
public override void ViewWillLayoutSubviews()
200200
{
201201
ConstrainItemsToBounds();
202-
203-
if (CollectionView is Items.MauiCollectionView { NeedsCellLayout: true } collectionView)
204-
{
205-
collectionView.NeedsCellLayout = false;
206-
}
207-
208202
base.ViewWillLayoutSubviews();
209203
InvalidateMeasureIfContentSizeChanged();
210204
LayoutEmptyView();

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

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

1212
WeakReference<ICustomMauiCollectionViewDelegate>? _customDelegate;
13-
14-
internal bool NeedsCellLayout { get; set; }
15-
1613
public MauiCollectionView(CGRect frame, UICollectionViewLayout layout) : base(frame, layout)
1714
{
1815
}
@@ -30,11 +27,10 @@ void IPlatformMeasureInvalidationController.InvalidateAncestorsMeasuresWhenMoved
3027

3128
void IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating)
3229
{
33-
if (isPropagating)
30+
if (!isPropagating)
3431
{
35-
NeedsCellLayout = true;
32+
SetNeedsLayout();
3633
}
37-
SetNeedsLayout();
3834
}
3935

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

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

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class StructuredItemsViewController<TItemsView> : ItemsViewController<TIt
2020

2121
UIView _footerUIView;
2222
VisualElement _footerViewFormsElement;
23+
bool _headerFooterPositionNeedLayoutUpdate;
2324

2425
public StructuredItemsViewController(TItemsView structuredItemsView, ItemsViewLayout layout)
2526
: base(structuredItemsView, layout)
@@ -86,23 +87,34 @@ protected override CGRect DetermineEmptyViewFrame()
8687

8788
public override void ViewWillLayoutSubviews()
8889
{
89-
var hasHeaderOrFooter = _footerViewFormsElement is not null || _headerViewFormsElement is not null;
90-
if (hasHeaderOrFooter && CollectionView is MauiCollectionView { NeedsCellLayout: true } collectionView)
90+
if (_headerFooterPositionNeedLayoutUpdate)
9191
{
92-
if (_headerViewFormsElement is not null)
92+
UpdateHeaderFooterPosition();
93+
base.ViewWillLayoutSubviews();
94+
}
95+
else
96+
{
97+
base.ViewWillLayoutSubviews();
98+
// This update is only relevant if you have a footer view because it's used to place the footer view
99+
// based on the ContentSize so we just update the positions if the ContentSize has changed
100+
if (_footerUIView != null)
93101
{
94-
RemeasureLayout(_headerViewFormsElement);
95-
}
102+
var emptyView = CollectionView.ViewWithTag(EmptyTag);
96103

97-
if (_footerViewFormsElement is not null)
98-
{
99-
RemeasureLayout(_footerViewFormsElement);
104+
if (IsHorizontal)
105+
{
106+
if (_footerUIView.Frame.X != ItemsViewLayout.CollectionViewContentSize.Width ||
107+
_footerUIView.Frame.X < emptyView?.Frame.X)
108+
UpdateHeaderFooterPosition();
109+
}
110+
else
111+
{
112+
if (_footerUIView.Frame.Y != ItemsViewLayout.CollectionViewContentSize.Height ||
113+
_footerUIView.Frame.Y < (emptyView?.Frame.Y + emptyView?.Frame.Height))
114+
UpdateHeaderFooterPosition();
115+
}
100116
}
101-
102-
UpdateHeaderFooterPosition();
103117
}
104-
105-
base.ViewWillLayoutSubviews();
106118
}
107119

108120
internal void UpdateFooterView()
@@ -154,8 +166,19 @@ internal void UpdateSubview(object view, DataTemplate viewTemplate, nint viewTag
154166
}
155167
}
156168

169+
157170
void UpdateHeaderFooterPosition()
158171
{
172+
// The Frame values for `CollectionView` aren't valid until the `CollectionView` has been added to the window
173+
// The initial frame is always just some default value that we will need to update once the CollectionView is added to the window
174+
if (CollectionView?.Window is null)
175+
{
176+
_headerFooterPositionNeedLayoutUpdate = true;
177+
return;
178+
}
179+
180+
_headerFooterPositionNeedLayoutUpdate = false;
181+
159182
var emptyView = CollectionView.ViewWithTag(EmptyTag);
160183

161184
if (IsHorizontal)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
namespace Maui.Controls.Sample.Issues;
2+
[Issue(IssueTracker.Github, 29051, "I8_Headers_and_Footers displays the footer 2019 in the middle of the header", PlatformAffected.iOS)]
3+
public class Issue29051 : ContentPage
4+
{
5+
public Issue29051()
6+
{
7+
var mainGrid = new Grid
8+
{
9+
Margin = new Thickness(20),
10+
RowDefinitions =
11+
{
12+
new RowDefinition { Height = GridLength.Auto },
13+
new RowDefinition { Height = GridLength.Star }
14+
}
15+
};
16+
17+
var instructionsStack = new StackLayout
18+
{
19+
Children =
20+
{
21+
new Label { Text = "1. The test passes if the view is displayed in Vertical list layout." },
22+
new Label { Text = "2. The test passes if the header text display 'Monkeys'." },
23+
new Label { Text = "3. The test passes if the footer text display '2019'." }
24+
}
25+
};
26+
27+
Grid.SetRow(instructionsStack, 0);
28+
mainGrid.Children.Add(instructionsStack);
29+
30+
var collectionView = new CollectionView1
31+
{
32+
Header = "Monkeys",
33+
Footer = "2019",
34+
ItemTemplate = new DataTemplate(() =>
35+
{
36+
var itemGrid = new Grid
37+
{
38+
Padding = new Thickness(10),
39+
RowDefinitions =
40+
{
41+
new RowDefinition { Height = GridLength.Auto },
42+
new RowDefinition { Height = GridLength.Auto }
43+
},
44+
ColumnDefinitions =
45+
{
46+
new ColumnDefinition { Width = GridLength.Auto },
47+
new ColumnDefinition { Width = GridLength.Star }
48+
}
49+
};
50+
51+
var nameLabel = new Label
52+
{
53+
FontAttributes = FontAttributes.Bold
54+
};
55+
nameLabel.SetBinding(Label.TextProperty, "Name");
56+
nameLabel.SetBinding(Label.AutomationIdProperty, "AutomationId");
57+
58+
Grid.SetColumn(nameLabel, 1);
59+
itemGrid.Children.Add(nameLabel);
60+
61+
return itemGrid;
62+
}),
63+
ItemsSource = Enumerable.Range(0, 5).Select(i => new { Name = $"Item {i}", AutomationId = $"Item{i}" }).ToList(),
64+
};
65+
66+
Grid.SetRow(collectionView, 1);
67+
mainGrid.Children.Add(collectionView);
68+
69+
Content = mainGrid;
70+
}
71+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using NUnit.Framework;
2+
using UITest.Appium;
3+
using UITest.Core;
4+
5+
namespace Microsoft.Maui.TestCases.Tests.Issues;
6+
public class Issue29051 : _IssuesUITest
7+
{
8+
public Issue29051(TestDevice testDevice) : base(testDevice)
9+
{
10+
}
11+
12+
public override string Issue => "I8_Headers_and_Footers displays the footer 2019 in the middle of the header";
13+
14+
[Test]
15+
[Category(UITestCategories.CollectionView)]
16+
public void FooterAndHeaderArePlacedCorrectlyAboveAndBelowCollectionView()
17+
{
18+
App.WaitForElement("Item1");
19+
VerifyScreenshot();
20+
}
21+
}

0 commit comments

Comments
 (0)