Skip to content

Commit

Permalink
Remove MeasureInvalidated propagation on non-legacy layouts
Browse files Browse the repository at this point in the history
  • Loading branch information
albyrock87 committed Nov 5, 2024
1 parent 8a11f59 commit 15d5364
Show file tree
Hide file tree
Showing 23 changed files with 234 additions and 200 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ void IMauiPlatformView.InvalidateAncestorsMeasuresWhenMovedToWindow()
_invalidateParentWhenMovedToWindow = true;
}

void IMauiPlatformView.InvalidateMeasure() => SetNeedsLayout();

public override void MovedToWindow()
{
base.MovedToWindow();
Expand Down
34 changes: 20 additions & 14 deletions src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,30 @@ public override void ViewWillAppear(bool animated)
public override void ViewWillLayoutSubviews()
{
ConstrainItemsToBounds();
InvalidateLayoutIfItemsMeasureChanged();
base.ViewWillLayoutSubviews();
InvalidateMeasureIfContentSizeChanged();
LayoutEmptyView();
}

void InvalidateLayoutIfItemsMeasureChanged()
{
var visibleCells = CollectionView.VisibleCells;

var changed = false;
for (int n = 0; n < visibleCells.Length; n++)
{
if (visibleCells[n] is TemplatedCell { MeasureInvalidated: true } cell && cell.VerifyAndUpdateSize())
{
changed = true;
}
}

if (changed)
{
ItemsViewLayout.InvalidateLayout();
}
}


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

[UnconditionalSuppressMessage("Memory", "MEM0003", Justification = "Proven safe in test: CollectionViewTests.ItemsSourceDoesNotLeak")]
void CellContentSizeChanged(object sender, EventArgs e)
{
if (_disposed)
return;

if (!(sender is TemplatedCell cell))
{
return;
}

var visibleCells = CollectionView.VisibleCells;

for (int n = 0; n < visibleCells.Length; n++)
{
if (cell == visibleCells[n])
{
ItemsViewLayout?.InvalidateLayout();
return;
}
}
CollectionView.SetNeedsLayout();
}

[UnconditionalSuppressMessage("Memory", "MEM0003", Justification = "Proven safe in test: CollectionViewTests.ItemsSourceDoesNotLeak")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ void IMauiPlatformView.InvalidateAncestorsMeasuresWhenMovedToWindow()
_invalidateParentWhenMovedToWindow = true;
}

void IMauiPlatformView.InvalidateMeasure()
{
// No-op during propagation
}

[UnconditionalSuppressMessage("Memory", "MEM0002", Justification = IUIViewLifeCycleEvents.UnconditionalSuppressMessage)]
EventHandler? _movedToWindow;

Expand Down
45 changes: 32 additions & 13 deletions src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace Microsoft.Maui.Controls.Handlers.Items
{
public abstract class TemplatedCell : ItemsViewCell
public abstract class TemplatedCell : ItemsViewCell, IMauiPlatformView
{
readonly WeakEventManager _weakEventManager = new();

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

internal CGSize CurrentSize => _size.ToCGSize();

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

WeakReference<IPlatformViewHandler> _handler;
bool _measureInvalidated;

internal bool MeasureInvalidated => _measureInvalidated;

internal IPlatformViewHandler PlatformHandler
{
Expand Down Expand Up @@ -77,9 +81,10 @@ protected void ClearConstraints()

internal void Unbind()
{
_bound = false;

if (PlatformHandler?.VirtualView is View view)
{
view.MeasureInvalidated -= MeasureInvalidated;
view.BindingContext = null;
}
}
Expand Down Expand Up @@ -120,6 +125,7 @@ CGSize UpdateCellSize()
var nativeBounds = platformView.Frame.ToRectangle();
PlatformHandler.VirtualView.Arrange(nativeBounds);
_size = nativeBounds.Size;
_measureInvalidated = false;

return size;
}
Expand Down Expand Up @@ -160,7 +166,6 @@ public void Bind(DataTemplate template, object bindingContext, ItemsView itemsVi
// Remove the old view, if it exists
if (oldElement != null)
{
oldElement.MeasureInvalidated -= MeasureInvalidated;
oldElement.BindingContext = null;
itemsView.RemoveLogicalChild(oldElement);
ClearSubviews();
Expand Down Expand Up @@ -190,13 +195,12 @@ public void Bind(DataTemplate template, object bindingContext, ItemsView itemsVi
if (oldElement != null)
{
oldElement.BindingContext = bindingContext;
oldElement.MeasureInvalidated += MeasureInvalidated;

UpdateCellSize();
}
}

CurrentTemplate = itemTemplate;
_bound = true;
((IMauiPlatformView)this).InvalidateMeasure();
}

void SetRenderer(IPlatformViewHandler renderer)
Expand All @@ -211,8 +215,6 @@ void SetRenderer(IPlatformViewHandler renderer)
InitializeContentConstraints(platformView);

UpdateVisualStates();

(renderer.VirtualView as View).MeasureInvalidated += MeasureInvalidated;
}

void ClearSubviews()
Expand All @@ -231,6 +233,8 @@ internal void UseContent(TemplatedCell measurementCell)
CurrentTemplate = measurementCell.CurrentTemplate;
_size = measurementCell._size;
SetRenderer(measurementCell.PlatformHandler);
_bound = true;
((IMauiPlatformView)this).InvalidateMeasure();
}

bool IsUsingVSMForSelectionColor(View view)
Expand Down Expand Up @@ -280,20 +284,30 @@ public override bool Selected

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

void MeasureInvalidated(object sender, EventArgs args)
void IMauiPlatformView.InvalidateMeasure()
{
// If the cell is not bound (or getting unbounded), we don't want to measure it
// and cause a useless and harming InvalidateLayout on the collection view layout
if (!_measureInvalidated && _bound)
{
_measureInvalidated = true;
OnContentSizeChanged();
}
}

internal bool VerifyAndUpdateSize()
{
_measureInvalidated = false;
var (needsUpdate, toSize) = NeedsContentSizeUpdate(_size);

if (!needsUpdate)
{
return;
return false;
}

// Cache the size for next time
_size = toSize;

// Let the controller know that things need to be arranged again
OnContentSizeChanged();
return true;
}

protected void OnContentSizeChanged()
Expand Down Expand Up @@ -344,5 +358,10 @@ void UpdateSelectionColor(View view)
SelectedBackgroundView.BackgroundColor = UIColor.Clear;
}
}

void IMauiPlatformView.InvalidateAncestorsMeasuresWhenMovedToWindow()
{
// This is a no-op for cells
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public override UICollectionViewCell GetCell(UICollectionView collectionView, NS
if (cell is TemplatedCell2 TemplatedCell2)
{
TemplatedCell2.ScrollDirection = ScrollDirection;

TemplatedCell2.Connect(CellContentSizeChanged);
TemplatedCell2.Bind(ItemsView.ItemTemplate, ItemsSource[indexpathAdjusted], ItemsView);
}
else if (cell is DefaultCell2 DefaultCell2)
Expand Down Expand Up @@ -188,10 +188,39 @@ public override void LoadView()

public override void ViewWillLayoutSubviews()
{
InvalidateLayoutIfItemsMeasureChanged();
base.ViewWillLayoutSubviews();
LayoutEmptyView();
}

void InvalidateLayoutIfItemsMeasureChanged()
{
var visibleCells = CollectionView.VisibleCells;
var changed = false;
for (int n = 0; n < visibleCells.Length; n++)
{
if (visibleCells[n] is TemplatedCell2 { MeasureInvalidated: true })
{
changed = true;
}
}

if (changed)
{
ItemsViewLayout.InvalidateLayout();
}
}

void CellContentSizeChanged(object sender, EventArgs e)
{
if (_disposed)
{
return;
}

CollectionView.SetNeedsLayout();
}

void Items.MauiCollectionView.ICustomMauiCollectionViewDelegate.MovedToWindow(UIView view)
{
if (CollectionView?.Window != null)
Expand Down
Loading

0 comments on commit 15d5364

Please sign in to comment.