Skip to content

Commit 4a71e68

Browse files
committed
Updated tests and improved solution
1 parent fbb04bd commit 4a71e68

File tree

10 files changed

+73
-74
lines changed

10 files changed

+73
-74
lines changed

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

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public abstract class ItemsViewController<TItemsView> : UICollectionViewControll
2929
protected ItemsViewLayout ItemsViewLayout { get; set; }
3030

3131
bool _initialized;
32+
bool _laidOut;
3233
bool _isEmpty = true;
3334
bool _emptyViewDisplayed;
3435
bool _disposed;
@@ -193,7 +194,6 @@ public override void LoadView()
193194
public override void ViewWillAppear(bool animated)
194195
{
195196
base.ViewWillAppear(animated);
196-
ConstrainItemsToBounds();
197197
}
198198

199199
public override void ViewWillLayoutSubviews()
@@ -203,24 +203,34 @@ public override void ViewWillLayoutSubviews()
203203
base.ViewWillLayoutSubviews();
204204
InvalidateMeasureIfContentSizeChanged();
205205
LayoutEmptyView();
206+
207+
_laidOut = true;
206208
}
207209

208210
void InvalidateLayoutIfItemsMeasureChanged()
209211
{
210212
var visibleCells = CollectionView.VisibleCells;
213+
List<NSIndexPath> invalidatedPaths = null;
211214

212-
var changed = false;
213-
for (int n = 0; n < visibleCells.Length; n++)
215+
var visibleCellsLength = visibleCells.Length;
216+
for (int n = 0; n < visibleCellsLength; n++)
214217
{
215-
if (visibleCells[n] is TemplatedCell { MeasureInvalidated: true } cell && cell.VerifyAndUpdateSize())
218+
if (visibleCells[n] is TemplatedCell { MeasureInvalidated: true } cell)
216219
{
217-
changed = true;
220+
invalidatedPaths ??= new List<NSIndexPath>(visibleCellsLength);
221+
var path = CollectionView.IndexPathForCell(cell);
222+
invalidatedPaths.Add(path);
218223
}
219224
}
220225

221-
if (changed)
226+
if (invalidatedPaths != null)
222227
{
223-
ItemsViewLayout.InvalidateLayout();
228+
var layoutInvalidationContext = new UICollectionViewLayoutInvalidationContext();
229+
layoutInvalidationContext.InvalidateItems(invalidatedPaths.ToArray());
230+
CollectionView.PerformBatchUpdates(() =>
231+
{
232+
CollectionView.CollectionViewLayout.InvalidateLayout(layoutInvalidationContext);
233+
}, null);
224234
}
225235
}
226236

@@ -302,7 +312,7 @@ void ConstrainItemsToBounds()
302312
{
303313
var contentBounds = CollectionView.AdjustedContentInset.InsetRect(CollectionView.Bounds);
304314
var constrainedSize = contentBounds.Size;
305-
ItemsViewLayout.UpdateConstraints(constrainedSize);
315+
ItemsViewLayout.UpdateConstraints(constrainedSize, !_laidOut);
306316
}
307317

308318
void EnsureLayoutInitialized()

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ protected virtual void HandlePropertyChanged(PropertyChangedEventArgs propertyCh
104104
}
105105
}
106106

107-
internal virtual bool UpdateConstraints(CGSize size)
107+
internal virtual bool UpdateConstraints(CGSize size, bool forceUpdate = false)
108108
{
109-
if (size.IsCloseTo(_currentSize))
109+
if (size.IsCloseTo(_currentSize) && !forceUpdate)
110110
{
111111
return false;
112112
}

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

Lines changed: 7 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -94,40 +94,26 @@ public override UICollectionViewLayoutAttributes PreferredLayoutAttributesFittin
9494
{
9595
var preferredAttributes = base.PreferredLayoutAttributesFittingAttributes(layoutAttributes);
9696

97-
var preferredSize = preferredAttributes.Frame.Size;
98-
99-
if (preferredSize.IsCloseTo(_size)
100-
&& AttributesConsistentWithConstrainedDimension(preferredAttributes))
97+
if (_measureInvalidated || !AttributesConsistentWithConstrainedDimension(preferredAttributes))
10198
{
102-
return preferredAttributes;
99+
UpdateCellSize();
103100
}
104101

105-
var size = UpdateCellSize();
106-
107102
// Adjust the preferred attributes to include space for the Forms element
108-
preferredAttributes.Frame = new CGRect(preferredAttributes.Frame.Location, size);
103+
preferredAttributes.Frame = new CGRect(preferredAttributes.Frame.Location, _size);
109104

110105
OnLayoutAttributesChanged(preferredAttributes);
111106

112107
return preferredAttributes;
113108
}
114109

115-
CGSize UpdateCellSize()
110+
void UpdateCellSize()
116111
{
117112
// Measure this cell (including the Forms element) if there is no constrained size
118113
var size = ConstrainedSize == default ? Measure() : ConstrainedSize;
119114

120-
// Update the size of the root view to accommodate the Forms element
121-
var platformView = PlatformHandler.ToPlatform();
122-
platformView.Frame = new CGRect(CGPoint.Empty, size);
123-
124-
// Layout the Maui element
125-
var nativeBounds = platformView.Frame.ToRectangle();
126-
PlatformHandler.VirtualView.Arrange(nativeBounds);
127-
_size = nativeBounds.Size;
115+
_size = size.ToSize();
128116
_measureInvalidated = false;
129-
130-
return size;
131117
}
132118

133119
[Obsolete]
@@ -146,6 +132,7 @@ protected void Layout(CGSize constraints)
146132
var rectangle = platformView.Frame.ToRectangle();
147133
PlatformHandler.VirtualView.Arrange(rectangle);
148134
_size = rectangle.Size;
135+
_measureInvalidated = false;
149136
}
150137

151138
public override void PrepareForReuse()
@@ -169,7 +156,6 @@ public void Bind(DataTemplate template, object bindingContext, ItemsView itemsVi
169156
oldElement.BindingContext = null;
170157
itemsView.RemoveLogicalChild(oldElement);
171158
ClearSubviews();
172-
_size = Size.Zero;
173159
}
174160

175161
// Create the content and renderer for the view
@@ -223,6 +209,7 @@ void SetRenderer(IPlatformViewHandler renderer)
223209
ClearSubviews();
224210

225211
InitializeContentConstraints(platformView);
212+
ContentView.MarkAsCrossPlatformLayoutBacking();
226213

227214
UpdateVisualStates();
228215
}
@@ -305,25 +292,6 @@ void IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating
305292
}
306293
}
307294

308-
internal bool VerifyAndUpdateSize()
309-
{
310-
_measureInvalidated = false;
311-
var (needsUpdate, toSize) = NeedsContentSizeUpdate(_size);
312-
313-
if (!needsUpdate)
314-
{
315-
return false;
316-
}
317-
318-
// Cache the size for next time
319-
_size = toSize;
320-
321-
// Notify size has changed
322-
OnContentSizeChanged();
323-
324-
return true;
325-
}
326-
327295
protected void OnContentSizeChanged()
328296
{
329297
_weakEventManager.HandleEvent(this, EventArgs.Empty, nameof(ContentSizeChanged));

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

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,19 +198,27 @@ public override void ViewWillLayoutSubviews()
198198
void InvalidateLayoutIfItemsMeasureChanged()
199199
{
200200
var visibleCells = CollectionView.VisibleCells;
201-
var changed = false;
202-
for (int n = 0; n < visibleCells.Length; n++)
201+
List<NSIndexPath> invalidatedPaths = null;
202+
203+
var visibleCellsLength = visibleCells.Length;
204+
for (int n = 0; n < visibleCellsLength; n++)
203205
{
204-
if (visibleCells[n] is TemplatedCell2 { MeasureInvalidated: true })
206+
if (visibleCells[n] is TemplatedCell2 { MeasureInvalidated: true } cell)
205207
{
206-
changed = true;
207-
break;
208+
invalidatedPaths ??= new List<NSIndexPath>(visibleCellsLength);
209+
var path = CollectionView.IndexPathForCell(cell);
210+
invalidatedPaths.Add(path);
208211
}
209212
}
210213

211-
if (changed)
214+
if (invalidatedPaths != null)
212215
{
213-
ItemsViewLayout.InvalidateLayout();
216+
var layoutInvalidationContext = new UICollectionViewLayoutInvalidationContext();
217+
layoutInvalidationContext.InvalidateItems(invalidatedPaths.ToArray());
218+
CollectionView.PerformBatchUpdates(() =>
219+
{
220+
CollectionView.CollectionViewLayout.InvalidateLayout(layoutInvalidationContext);
221+
}, null);
214222
}
215223
}
216224

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ void BindVirtualView(View virtualView, object bindingContext, ItemsView itemsVie
144144

145145
PlatformHandler = virtualView.Handler as IPlatformViewHandler;
146146
InitializeContentConstraints(PlatformView);
147+
ContentView.MarkAsCrossPlatformLayoutBacking();
147148

148149
virtualView.BindingContext = bindingContext;
149150
itemsView.AddLogicalChild(virtualView);

src/Controls/tests/TestCases.HostApp/Issues/Issue25671.xaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313
AbsoluteLayout.LayoutBounds="0,0,1,1">
1414

1515
<i:Issue25671ContentView>
16-
<i:Issue25671Label Text="Hello world" Padding="16,8" TextColor="White" />
16+
<i:Issue25671Label Text="Hello world" Padding="16,8" TextColor="White" x:Name="HeadingLabel" AutomationId="HeadingLabel" />
1717
</i:Issue25671ContentView>
1818

19-
<i:Issue25671CollectionView x:Name="CV" AutomationId="CV" Grid.Row="1">
20-
<i:Issue25671CollectionView.ItemTemplate>
19+
<CollectionView x:Name="CV" AutomationId="CV" Grid.Row="1">
20+
<CollectionView.ItemTemplate>
2121
<DataTemplate>
2222
<i:Issue25671ContentView Padding="16">
2323
<i:Issue25671VerticalStackLayout Shadow="{Shadow Radius=16, Brush=Black, Opacity=0.24}"
@@ -33,8 +33,8 @@
3333
</i:Issue25671VerticalStackLayout>
3434
</i:Issue25671ContentView>
3535
</DataTemplate>
36-
</i:Issue25671CollectionView.ItemTemplate>
37-
</i:Issue25671CollectionView>
36+
</CollectionView.ItemTemplate>
37+
</CollectionView>
3838

3939
<i:Issue25671VerticalStackLayout Grid.Row="2" BackgroundColor="DarkBlue">
4040
<i:Issue25671Button Text="Regenerate items" Padding="16,8" TextColor="White" Clicked="RegenerateItems" AutomationId="RegenerateItems" />

src/Controls/tests/TestCases.HostApp/Issues/Issue25671.xaml.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ public Issue25671()
1717
{
1818
InitializeComponent();
1919
GenerateItems();
20+
CV.HandlerChanged += (s, e) =>
21+
{
22+
if (CV.Handler is { } handler)
23+
{
24+
HeadingLabel.Text = handler.GetType().Name;
25+
}
26+
};
2027
}
2128

2229
void RegenerateItems(object sender, EventArgs args)
@@ -41,15 +48,6 @@ void GenerateItems()
4148
}
4249
}
4350

44-
#if IOS
45-
// When CV2 is completed and can handle resize of items we can remove this pointer to CV1
46-
internal class Issue25671CollectionView : CollectionView1
47-
#else
48-
public class Issue25671CollectionView : CollectionView
49-
#endif
50-
{
51-
}
52-
5351
public class Issue25671AbsoluteLayout : AbsoluteLayout
5452
{
5553
public static long MeasurePasses = 0;

src/Controls/tests/TestCases.HostApp/Issues/XFIssue/Issue13203.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ protected override void Init()
1111
{
1212
IsVisible = false,
1313

14+
BackgroundColor = Colors.PaleGreen,
1415
ItemTemplate = new DataTemplate(() =>
1516
{
16-
var label = new Label();
17+
var label = new Label { FontSize = 40, BackgroundColor = Colors.SeaGreen };
1718
label.SetBinding(Label.TextProperty, new Binding(nameof(Item.Text)));
1819
label.SetBinding(Label.AutomationIdProperty, new Binding(nameof(Item.Text)));
1920
return label;

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,14 @@ public async Task LayoutPassesShouldNotIncrease()
4242
var arrangePasses = int.Parse(match.Groups[2].Value);
4343

4444
#if IOS
45-
const int maxMeasurePasses = 525;
46-
const int maxArrangePasses = 308;
45+
var maxMeasurePasses = 221;
46+
var maxArrangePasses = 185;
47+
48+
if (App.FindElement("HeadingLabel").GetText() == "CollectionViewHandler2")
49+
{
50+
maxMeasurePasses = 354;
51+
maxArrangePasses = 313;
52+
}
4753
#elif ANDROID
4854
const int maxMeasurePasses = 353;
4955
const int maxArrangePasses = 337;

src/Core/src/Platform/iOS/ViewExtensions.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,13 @@ internal static float GetDisplayDensity(this UIView? view) =>
992992

993993
internal static bool IsSoftInputShowing(this UIView inputView) => inputView.IsFirstResponder;
994994

995-
internal static bool IsFinalMeasureHandledBySuperView(this UIView? view) => view?.Superview is ICrossPlatformLayoutBacking { CrossPlatformLayout: not null };
995+
private const nint NativeViewControlledByCrossPlatformLayout = 0x63D2A1;
996+
997+
internal static bool IsFinalMeasureHandledBySuperView(this UIView? view) => view?.Superview is ICrossPlatformLayoutBacking { CrossPlatformLayout: not null } or { Tag: NativeViewControlledByCrossPlatformLayout };
998+
999+
internal static void MarkAsCrossPlatformLayoutBacking(this UIView view)
1000+
{
1001+
view.Tag = NativeViewControlledByCrossPlatformLayout;
1002+
}
9961003
}
9971004
}

0 commit comments

Comments
 (0)