Skip to content

Commit 23499a5

Browse files
committed
Skip measure invalidated propagation by default
1 parent 1412b38 commit 23499a5

File tree

39 files changed

+715
-392
lines changed

39 files changed

+715
-392
lines changed

src/Controls/src/Core/Compatibility/Handlers/iOS/FrameRenderer.cs

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -218,33 +218,6 @@ protected override void Dispose(bool disposing)
218218
}
219219
}
220220

221-
bool _pendingSuperViewSetNeedsLayout;
222-
223-
public override void SetNeedsLayout()
224-
{
225-
base.SetNeedsLayout();
226-
227-
if (Window is not null)
228-
{
229-
_pendingSuperViewSetNeedsLayout = false;
230-
this.Superview?.SetNeedsLayout();
231-
}
232-
else{
233-
_pendingSuperViewSetNeedsLayout = true;
234-
}
235-
}
236-
237-
public override void MovedToWindow()
238-
{
239-
base.MovedToWindow();
240-
if (_pendingSuperViewSetNeedsLayout)
241-
{
242-
this.Superview?.SetNeedsLayout();
243-
}
244-
245-
_pendingSuperViewSetNeedsLayout = false;
246-
}
247-
248221
[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
249222
class FrameView : Microsoft.Maui.Platform.ContentView
250223
{

src/Controls/src/Core/Compatibility/Handlers/iOS/VisualElementRenderer.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
using System;
22
using System.ComponentModel;
33
using Microsoft.Maui.Controls.Platform;
4-
using Microsoft.Maui.Graphics;
54
using UIKit;
65

76
namespace Microsoft.Maui.Controls.Handlers.Compatibility
87
{
9-
public abstract partial class VisualElementRenderer<TElement> : UIView, IPlatformViewHandler, IElementHandler
8+
public abstract partial class VisualElementRenderer<TElement> : UIView, IPlatformViewHandler, IElementHandler, IMauiPlatformView
109
where TElement : Element, IView
1110
{
11+
bool _invalidateParentWhenMovedToWindow;
1212
object? IElementHandler.PlatformView => Subviews.Length > 0 ? Subviews[0] : this;
1313

1414
public virtual UIViewController? ViewController => null;
@@ -93,5 +93,22 @@ void OnSizeChanged(object? sender, EventArgs e)
9393
{
9494
UpdateNativeWidget();
9595
}
96+
97+
void IMauiPlatformView.InvalidateAncestorsMeasuresWhenMovedToWindow()
98+
{
99+
_invalidateParentWhenMovedToWindow = true;
100+
}
101+
102+
void IMauiPlatformView.InvalidateMeasure(bool isPropagating) => SetNeedsLayout();
103+
104+
public override void MovedToWindow()
105+
{
106+
base.MovedToWindow();
107+
if (_invalidateParentWhenMovedToWindow)
108+
{
109+
_invalidateParentWhenMovedToWindow = false;
110+
this.InvalidateAncestorsMeasures();
111+
}
112+
}
96113
}
97114
}

src/Controls/src/Core/ContentPresenter.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,5 @@ Size ICrossPlatformLayout.CrossPlatformArrange(Rect bounds)
130130
this.ArrangeContent(bounds);
131131
return bounds.Size;
132132
}
133-
134-
private protected override void InvalidateMeasureLegacy(InvalidationTrigger trigger, int depth, int depthLeveltoInvalidate)
135-
{
136-
base.InvalidateMeasureLegacy(trigger, depth, 1);
137-
}
138133
}
139134
}

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

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,30 @@ public override void ViewWillAppear(bool animated)
194194
public override void ViewWillLayoutSubviews()
195195
{
196196
ConstrainItemsToBounds();
197+
InvalidateLayoutIfItemsMeasureChanged();
197198
base.ViewWillLayoutSubviews();
198199
InvalidateMeasureIfContentSizeChanged();
199200
LayoutEmptyView();
200201
}
201202

203+
void InvalidateLayoutIfItemsMeasureChanged()
204+
{
205+
var visibleCells = CollectionView.VisibleCells;
206+
207+
var changed = false;
208+
for (int n = 0; n < visibleCells.Length; n++)
209+
{
210+
if (visibleCells[n] is TemplatedCell { MeasureInvalidated: true } cell && cell.VerifyAndUpdateSize())
211+
{
212+
changed = true;
213+
}
214+
}
215+
216+
if (changed)
217+
{
218+
ItemsViewLayout.InvalidateLayout();
219+
}
220+
}
202221

203222

204223
void MauiCollectionView.ICustomMauiCollectionViewDelegate.MovedToWindow(UIView view)
@@ -402,27 +421,14 @@ protected object GetItemAtIndex(NSIndexPath index)
402421
return ItemsSource[index];
403422
}
404423

405-
[UnconditionalSuppressMessage("Memory", "MEM0003", Justification = "Proven safe in test: CollectionViewTests.ItemsSourceDoesNotLeak")]
406424
void CellContentSizeChanged(object sender, EventArgs e)
407425
{
408426
if (_disposed)
409-
return;
410-
411-
if (!(sender is TemplatedCell cell))
412427
{
413428
return;
414429
}
415430

416-
var visibleCells = CollectionView.VisibleCells;
417-
418-
for (int n = 0; n < visibleCells.Length; n++)
419-
{
420-
if (cell == visibleCells[n])
421-
{
422-
ItemsViewLayout?.InvalidateLayout();
423-
return;
424-
}
425-
}
431+
CollectionView.SetNeedsLayout();
426432
}
427433

428434
[UnconditionalSuppressMessage("Memory", "MEM0003", Justification = "Proven safe in test: CollectionViewTests.ItemsSourceDoesNotLeak")]

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55

66
namespace Microsoft.Maui.Controls.Handlers.Items;
77

8-
internal class MauiCollectionView : UICollectionView, IUIViewLifeCycleEvents
8+
internal class MauiCollectionView : UICollectionView, IUIViewLifeCycleEvents, IMauiPlatformView
99
{
10+
bool _invalidateParentWhenMovedToWindow;
11+
1012
WeakReference<ICustomMauiCollectionViewDelegate>? _customDelegate;
1113
public MauiCollectionView(CGRect frame, UICollectionViewLayout layout) : base(frame, layout)
1214
{
@@ -18,8 +20,22 @@ public override void ScrollRectToVisible(CGRect rect, bool animated)
1820
base.ScrollRectToVisible(rect, animated);
1921
}
2022

23+
void IMauiPlatformView.InvalidateAncestorsMeasuresWhenMovedToWindow()
24+
{
25+
_invalidateParentWhenMovedToWindow = true;
26+
}
27+
28+
void IMauiPlatformView.InvalidateMeasure(bool isPropagating)
29+
{
30+
if (!isPropagating)
31+
{
32+
SetNeedsLayout();
33+
}
34+
}
35+
2136
[UnconditionalSuppressMessage("Memory", "MEM0002", Justification = IUIViewLifeCycleEvents.UnconditionalSuppressMessage)]
2237
EventHandler? _movedToWindow;
38+
2339
event EventHandler? IUIViewLifeCycleEvents.MovedToWindow
2440
{
2541
add => _movedToWindow += value;
@@ -35,6 +51,12 @@ public override void MovedToWindow()
3551
{
3652
target.MovedToWindow(this);
3753
}
54+
55+
if (_invalidateParentWhenMovedToWindow)
56+
{
57+
_invalidateParentWhenMovedToWindow = false;
58+
this.InvalidateAncestorsMeasures();
59+
}
3860
}
3961

4062
internal void SetCustomDelegate(ICustomMauiCollectionViewDelegate customDelegate)

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

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace Microsoft.Maui.Controls.Handlers.Items
1111
{
12-
public abstract class TemplatedCell : ItemsViewCell
12+
public abstract class TemplatedCell : ItemsViewCell, IMauiPlatformView
1313
{
1414
readonly WeakEventManager _weakEventManager = new();
1515

@@ -40,6 +40,7 @@ public DataTemplate CurrentTemplate
4040
// Keep track of the cell size so we can verify whether a measure invalidation
4141
// actually changed the size of the cell
4242
Size _size;
43+
bool _bound;
4344

4445
internal CGSize CurrentSize => _size.ToCGSize();
4546

@@ -50,6 +51,9 @@ protected TemplatedCell(CGRect frame) : base(frame)
5051
}
5152

5253
WeakReference<IPlatformViewHandler> _handler;
54+
bool _measureInvalidated;
55+
56+
internal bool MeasureInvalidated => _measureInvalidated;
5357

5458
internal IPlatformViewHandler PlatformHandler
5559
{
@@ -77,9 +81,10 @@ protected void ClearConstraints()
7781

7882
internal void Unbind()
7983
{
84+
_bound = false;
85+
8086
if (PlatformHandler?.VirtualView is View view)
8187
{
82-
view.MeasureInvalidated -= MeasureInvalidated;
8388
view.BindingContext = null;
8489
}
8590
}
@@ -120,6 +125,7 @@ CGSize UpdateCellSize()
120125
var nativeBounds = platformView.Frame.ToRectangle();
121126
PlatformHandler.VirtualView.Arrange(nativeBounds);
122127
_size = nativeBounds.Size;
128+
_measureInvalidated = false;
123129

124130
return size;
125131
}
@@ -160,7 +166,6 @@ public void Bind(DataTemplate template, object bindingContext, ItemsView itemsVi
160166
// Remove the old view, if it exists
161167
if (oldElement != null)
162168
{
163-
oldElement.MeasureInvalidated -= MeasureInvalidated;
164169
oldElement.BindingContext = null;
165170
itemsView.RemoveLogicalChild(oldElement);
166171
ClearSubviews();
@@ -190,13 +195,12 @@ public void Bind(DataTemplate template, object bindingContext, ItemsView itemsVi
190195
if (oldElement != null)
191196
{
192197
oldElement.BindingContext = bindingContext;
193-
oldElement.MeasureInvalidated += MeasureInvalidated;
194-
195-
UpdateCellSize();
196198
}
197199
}
198200

199201
CurrentTemplate = itemTemplate;
202+
_bound = true;
203+
((IMauiPlatformView)this).InvalidateMeasure();
200204
}
201205

202206
void SetRenderer(IPlatformViewHandler renderer)
@@ -211,8 +215,6 @@ void SetRenderer(IPlatformViewHandler renderer)
211215
InitializeContentConstraints(platformView);
212216

213217
UpdateVisualStates();
214-
215-
(renderer.VirtualView as View).MeasureInvalidated += MeasureInvalidated;
216218
}
217219

218220
void ClearSubviews()
@@ -231,6 +233,8 @@ internal void UseContent(TemplatedCell measurementCell)
231233
CurrentTemplate = measurementCell.CurrentTemplate;
232234
_size = measurementCell._size;
233235
SetRenderer(measurementCell.PlatformHandler);
236+
_bound = true;
237+
((IMauiPlatformView)this).InvalidateMeasure();
234238
}
235239

236240
bool IsUsingVSMForSelectionColor(View view)
@@ -280,20 +284,30 @@ public override bool Selected
280284

281285
protected abstract (bool, Size) NeedsContentSizeUpdate(Size currentSize);
282286

283-
void MeasureInvalidated(object sender, EventArgs args)
287+
void IMauiPlatformView.InvalidateMeasure(bool isPropagating)
288+
{
289+
// If the cell is not bound (or getting unbounded), we don't want to measure it
290+
// and cause a useless and harming InvalidateLayout on the collection view layout
291+
if (!_measureInvalidated && _bound)
292+
{
293+
_measureInvalidated = true;
294+
OnContentSizeChanged();
295+
}
296+
}
297+
298+
internal bool VerifyAndUpdateSize()
284299
{
300+
_measureInvalidated = false;
285301
var (needsUpdate, toSize) = NeedsContentSizeUpdate(_size);
286302

287303
if (!needsUpdate)
288304
{
289-
return;
305+
return false;
290306
}
291307

292308
// Cache the size for next time
293309
_size = toSize;
294-
295-
// Let the controller know that things need to be arranged again
296-
OnContentSizeChanged();
310+
return true;
297311
}
298312

299313
protected void OnContentSizeChanged()
@@ -344,5 +358,10 @@ void UpdateSelectionColor(View view)
344358
SelectedBackgroundView.BackgroundColor = UIColor.Clear;
345359
}
346360
}
361+
362+
void IMauiPlatformView.InvalidateAncestorsMeasuresWhenMovedToWindow()
363+
{
364+
// This is a no-op for cells
365+
}
347366
}
348367
}

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public override UICollectionViewCell GetCell(UICollectionView collectionView, NS
112112
if (cell is TemplatedCell2 TemplatedCell2)
113113
{
114114
TemplatedCell2.ScrollDirection = ScrollDirection;
115-
115+
TemplatedCell2.Connect(CellContentSizeChanged);
116116
TemplatedCell2.Bind(ItemsView.ItemTemplate, ItemsSource[indexpathAdjusted], ItemsView);
117117
}
118118
else if (cell is DefaultCell2 DefaultCell2)
@@ -188,10 +188,39 @@ public override void LoadView()
188188

189189
public override void ViewWillLayoutSubviews()
190190
{
191+
InvalidateLayoutIfItemsMeasureChanged();
191192
base.ViewWillLayoutSubviews();
192193
LayoutEmptyView();
193194
}
194195

196+
void InvalidateLayoutIfItemsMeasureChanged()
197+
{
198+
var visibleCells = CollectionView.VisibleCells;
199+
var changed = false;
200+
for (int n = 0; n < visibleCells.Length; n++)
201+
{
202+
if (visibleCells[n] is TemplatedCell2 { MeasureInvalidated: true })
203+
{
204+
changed = true;
205+
}
206+
}
207+
208+
if (changed)
209+
{
210+
ItemsViewLayout.InvalidateLayout();
211+
}
212+
}
213+
214+
void CellContentSizeChanged(object sender, EventArgs e)
215+
{
216+
if (_disposed)
217+
{
218+
return;
219+
}
220+
221+
CollectionView.SetNeedsLayout();
222+
}
223+
195224
void Items.MauiCollectionView.ICustomMauiCollectionViewDelegate.MovedToWindow(UIView view)
196225
{
197226
if (CollectionView?.Window != null)

0 commit comments

Comments
 (0)