Fix iOS CollectionView stale layout invalidations#35245
Conversation
There are two related races in the iOS CollectionView handler when the item source changes while cells are still participating in UIKit layout. The first race happens after a visible templated cell invalidates its measure. MAUI records that state on the cell and the next ViewWillLayoutSubviews pass asks UICollectionViewFlowLayout to invalidate exactly those changed items. Previously the handler collected the cells first and converted them back to index paths later, at the point where the invalidation context was created. If the bound ItemsSource had changed in the meantime, for example an observable source inserted items and the view then cleared or replaced ItemsSource before the next layout pass, UIKit could still report a visible cell whose current index path no longer existed in MAUI's new ItemsSource. Passing that stale index path to InvalidateItems leaves UICollectionView and the data source with inconsistent item counts and can crash during layout. Fix that path by resolving each invalidated visible cell to an NSIndexPath immediately and keeping only paths that are still valid for the current ItemsSource. The invalidation context is then built from the validated paths. This preserves targeted invalidation for normal measure changes, while dropping cells that belong to the previous source state and cannot be safely invalidated by item path anymore. The second race involves measurement cells. ItemsViewController keeps prototype templated cells in _measurementCells so their realized content can be transferred to real UICollectionView cells. When an ItemsSource update, empty-source transition, or source disposal clears that dictionary, the old code only removed the references. Those measurement cells could still be bound to item view models and still subscribed to LayoutAttributesChanged. Later binding or property changes from that stale content could propagate measure/layout invalidations through cells that are no longer owned by the active source state. In the worst case this combines with UIKit's pending layout work after ReloadData or source clearing and contributes to the same stale layout invalidation problem; it can also keep disconnected measurement content behaving as if it were still live. Fix that by centralizing measurement-cell clearing. Before the cache is cleared, each cached cell is detached from the layout-attribute event and unbound so its BindingContext is removed and future measure invalidations from that stale measured content do not flow back into the CollectionView layout. The regression test reproduces the important ordering: a templated CollectionView is displayed, a visible label changes text so the cell measure is invalidated, the observable source mutates, and ItemsSource is immediately cleared before UIKit finishes its next layout pass. The test forces layout afterward and verifies this no longer crashes.
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35245Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35245" |
There was a problem hiding this comment.
Pull request overview
Addresses iOS CollectionView layout invalidation races when the ItemsSource changes during/around UIKit layout passes, aiming to prevent stale index-path invalidations and detach stale measurement cells to avoid post-clear invalidations and crashes.
Changes:
- Validates visible-cell index paths before building a targeted
UICollectionViewFlowLayoutInvalidationContext. - Centralizes measurement-cell cache clearing to also detach event handlers and unbind cached cells.
- Adds an iOS device regression test that exercises “measure invalidation + ItemsSource mutation + ItemsSource clear” ordering.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs | Filters/records index paths earlier during layout invalidation and introduces ClearMeasurementCells() to detach/unbind cached measurement cells. |
| src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs | Adds a regression test intended to reproduce the stale invalidation crash scenario. |
There was a problem hiding this comment.
Pull request overview
Fixes a race in the iOS CollectionView handlers where pending UIKit layout passes can attempt targeted invalidations using stale index paths after an ItemsSource change, potentially crashing during layout. Also ensures measurement/prototype cells are properly detached/unbound when clearing their cache to prevent stale invalidations from orphaned content.
Changes:
- Validate and capture
NSIndexPathvalues immediately forMeasureInvalidatedvisible cells (dropping stale paths) before callingInvalidateItems. - Centralize and harden measurement-cell cache clearing by detaching
LayoutAttributesChangedand unbinding cached cells before clearing. - Add a device regression test that exercises the stale-invalidation timing (runs against both
CollectionViewHandlerandCollectionViewHandler2).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs | Validates index paths before invalidation and safely clears measurement cells by unbinding/detaching. |
| src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs | Applies the same index-path validation approach to the Handler2 iOS implementation. |
| src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs | Adds a regression device test for the stale invalidation crash scenario for both handlers. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
MauiBot
left a comment
There was a problem hiding this comment.
🤖 Automated review — alternative fix proposed
The expert-reviewer evaluation compared the PR fix against #2 automatically generated candidates and selected try-fix-2 as the strongest fix.
Why: The PR's Gate failed on iOS device tests, making try-fix-2 the best validated alternative. try-fix-2's early-exit source-empty guard (if (ItemsSource.ItemCount == 0) return) is the simplest passing fix — 14 lines across 2 files — that directly prevents the crash at the exact UIKit contract violation point. Two other try-fix approaches also passed, but try-fix-2 is the most minimal with zero side effects.
Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.
Candidate diff (`try-fix-2`)
diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
index 04d41a5607..dfda1143dd 100644
--- a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
+++ b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
@@ -256,6 +256,13 @@ namespace Microsoft.Maui.Controls.Handlers.Items
void InvalidateLayoutIfItemsMeasureChanged()
{
+ // When the source has no items, any visible cells with MeasureInvalidated=true belong to the
+ // previous source and are still animating out. Passing their stale index paths to
+ // InvalidateItems would throw NSInternalInconsistencyException because UIKit has already
+ // committed the reload to zero items.
+ if (ItemsSource.ItemCount == 0)
+ return;
+
var visibleCells = CollectionView.VisibleCells;
List<TemplatedCell> invalidatedCells = null;
diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
index a92f32bc46..f5e854885c 100644
--- a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
+++ b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
@@ -209,6 +209,13 @@ namespace Microsoft.Maui.Controls.Handlers.Items2
void InvalidateLayoutIfItemsMeasureChanged()
{
+ // When the source has no items, any visible cells with MeasureInvalidated=true belong to the
+ // previous source and are still animating out. Passing their stale index paths to
+ // InvalidateItems would throw NSInternalInconsistencyException because UIKit has already
+ // committed the reload to zero items.
+ if (ItemsSource.ItemCount == 0)
+ return;
+
var collectionView = CollectionView;
var visibleCells = collectionView.VisibleCells;
List<TemplatedCell2> invalidatedCells = null;
kubaflo
left a comment
There was a problem hiding this comment.
Could you please try ai's suggestions?
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 7 findings
See inline comments for details.
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 5 findings
See inline comments for details.
| ItemsViewLayout.PrepareCellForLayout(cell); | ||
| } | ||
|
|
||
| void ClearMeasurementCells() |
There was a problem hiding this comment.
This is generally true also for non-measurement cells and possible wider scope bug that should be fixed separately. (In our app we diligently reset ItemSource to null to solve this.)
MauiBot
left a comment
There was a problem hiding this comment.
🤖 Automated review — alternative fix proposed
The expert-reviewer evaluation compared the PR fix against #2 automatically generated candidates and selected try-fix-2 as the strongest fix.
Why: try-fix-2 is the only empirically validated candidate (923/923 device tests passing). It takes a prevention-over-filtering approach: by clearing NeedsCellLayout before the source swap, InvalidateLayoutIfItemsMeasureChanged never runs during the transitional state, eliminating the stale-index-path crash at its root. A source-generation counter provides lightweight lazy invalidation for stale measurement-cell callbacks without requiring eager unbind of all cells.
Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.
Candidate diff (`try-fix-2`)
diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
index 04d41a5607..80f62912d2 100644
--- a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
+++ b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
@@ -45,6 +45,8 @@ namespace Microsoft.Maui.Controls.Handlers.Items
UIView _emptyUIView;
VisualElement _emptyViewFormsElement;
Dictionary<object, TemplatedCell> _measurementCells = new Dictionary<object, TemplatedCell>();
+ readonly Dictionary<TemplatedCell, int> _measurementCellGenerations = new();
+ int _sourceGeneration = 0;
List<string> _cellReuseIds = new List<string>();
[UnconditionalSuppressMessage("Memory", "MEM0002", Justification = "Proven safe in test: MemoryTests.HandlerDoesNotLeak")]
@@ -419,6 +421,9 @@ namespace Microsoft.Maui.Controls.Handlers.Items
public virtual void UpdateItemsSource()
{
+ if (CollectionView is MauiCollectionView mauiCV)
+ mauiCV.NeedsCellLayout = false;
+ _sourceGeneration++;
_measurementCells?.Clear();
ItemsViewLayout?.ClearCellSizeCache();
ItemsSource?.Dispose();
@@ -432,6 +437,9 @@ namespace Microsoft.Maui.Controls.Handlers.Items
internal void DisposeItemsSource()
{
+ if (CollectionView is MauiCollectionView mauiCV)
+ mauiCV.NeedsCellLayout = false;
+ _sourceGeneration++;
_measurementCells?.Clear();
ItemsViewLayout?.ClearCellSizeCache();
ItemsSource?.Dispose();
@@ -494,6 +502,9 @@ namespace Microsoft.Maui.Controls.Handlers.Items
protected virtual void UpdateTemplatedCell(TemplatedCell cell, NSIndexPath indexPath)
{
cell.LayoutAttributesChanged -= CellLayoutAttributesChanged;
+ // If this cell was previously a measurement cell in an older generation, remove its stale entry
+ // so its future LayoutAttributesChanged events are processed normally in the new source context.
+ _measurementCellGenerations.Remove(cell);
var bindingContext = ItemsSource[indexPath];
@@ -501,6 +512,7 @@ namespace Microsoft.Maui.Controls.Handlers.Items
if (_measurementCells != null && _measurementCells.TryGetValue(bindingContext, out TemplatedCell measurementCell))
{
_measurementCells.Remove(bindingContext);
+ _measurementCellGenerations.Remove(measurementCell);
measurementCell.LayoutAttributesChanged -= CellLayoutAttributesChanged;
cell.UseContent(measurementCell);
}
@@ -527,6 +539,14 @@ namespace Microsoft.Maui.Controls.Handlers.Items
[UnconditionalSuppressMessage("Memory", "MEM0003", Justification = "Proven safe in test: CollectionViewTests.ItemsSourceDoesNotLeak")]
void CellLayoutAttributesChanged(object sender, LayoutAttributesChangedEventArgs args)
{
+ if (sender is TemplatedCell cell &&
+ _measurementCellGenerations.TryGetValue(cell, out int gen) &&
+ gen != _sourceGeneration)
+ {
+ _measurementCellGenerations.Remove(cell);
+ return;
+ }
+
CacheCellAttributes(args.NewAttributes.IndexPath, args.NewAttributes.Size);
}
@@ -902,7 +922,10 @@ namespace Microsoft.Maui.Controls.Handlers.Items
// Keep this cell around, we can transfer the contents to the actual cell when the UICollectionView creates it
if (_measurementCells != null)
+ {
_measurementCells[ItemsSource[indexPath]] = templatedCell;
+ _measurementCellGenerations[templatedCell] = _sourceGeneration;
+ }
return templatedCell;
}
diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
index a92f32bc46..f724bcc49a 100644
--- a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
+++ b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
@@ -256,6 +256,8 @@ namespace Microsoft.Maui.Controls.Handlers.Items2
internal void DisposeItemsSource()
{
+ if (CollectionView is Items.MauiCollectionView mauiCV)
+ mauiCV.NeedsCellLayout = false;
ItemsSource?.Dispose();
ItemsSource = new Items.EmptySource();
ReloadData();
@@ -290,6 +292,8 @@ namespace Microsoft.Maui.Controls.Handlers.Items2
public virtual void UpdateItemsSource()
{
+ if (CollectionView is Items.MauiCollectionView mauiCV)
+ mauiCV.NeedsCellLayout = false;
ItemsSource?.Dispose();
ItemsSource = CreateItemsViewSource();
MauiBot
left a comment
There was a problem hiding this comment.
🤖 Automated review — alternative fix proposed
The expert-reviewer evaluation compared the PR fix against #2 automatically generated candidates and selected try-fix-2 as the strongest fix.
Why: try-fix-2 is the only empirically verified passing fix: it resets MeasureInvalidated on all visible cells before source swap, preventing stale cells from ever being candidates for IndexPath invalidation. It also includes proper CleanupMeasurementCells (unsubscription + Unbind) and closes the Dispose() gap that the PR leaves open. Both regression tests passed with 923 total tests.
Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.
Candidate diff (`try-fix-2`)
diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
index 04d41a5607..c4c505e663 100644
--- a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
+++ b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
@@ -88,6 +88,7 @@ namespace Microsoft.Maui.Controls.Handlers.Items
if (disposing)
{
+ CleanupMeasurementCells();
ItemsSource?.Dispose();
((IUIViewLifeCycleEvents)CollectionView).MovedToWindow -= MovedToWindow;
@@ -139,7 +140,8 @@ namespace Microsoft.Maui.Controls.Handlers.Items
if (_isEmpty)
{
- _measurementCells?.Clear();
+ ResetVisibleCellsMeasureInvalidated();
+ CleanupMeasurementCells();
ItemsViewLayout?.ClearCellSizeCache();
}
@@ -419,7 +421,8 @@ namespace Microsoft.Maui.Controls.Handlers.Items
public virtual void UpdateItemsSource()
{
- _measurementCells?.Clear();
+ ResetVisibleCellsMeasureInvalidated();
+ CleanupMeasurementCells();
ItemsViewLayout?.ClearCellSizeCache();
ItemsSource?.Dispose();
ItemsSource = CreateItemsViewSource();
@@ -432,7 +435,8 @@ namespace Microsoft.Maui.Controls.Handlers.Items
internal void DisposeItemsSource()
{
- _measurementCells?.Clear();
+ ResetVisibleCellsMeasureInvalidated();
+ CleanupMeasurementCells();
ItemsViewLayout?.ClearCellSizeCache();
ItemsSource?.Dispose();
ItemsSource = new EmptySource();
@@ -519,6 +523,34 @@ namespace Microsoft.Maui.Controls.Handlers.Items
return ItemsSource.GetIndexForItem(item);
}
+ void ResetVisibleCellsMeasureInvalidated()
+ {
+ var visibleCells = CollectionView?.VisibleCells;
+ if (visibleCells is null)
+ return;
+
+ for (int i = 0; i < visibleCells.Length; i++)
+ {
+ if (visibleCells[i] is TemplatedCell cell)
+ cell.ResetMeasureInvalidated();
+ }
+ }
+
+ void CleanupMeasurementCells()
+ {
+ if (_measurementCells is null)
+ return;
+
+ foreach (var measurementCell in _measurementCells.Values)
+ {
+ measurementCell.LayoutAttributesChanged -= CellLayoutAttributesChanged;
+ measurementCell.ResetMeasureInvalidated();
+ measurementCell.Unbind();
+ }
+
+ _measurementCells.Clear();
+ }
+
protected object GetItemAtIndex(NSIndexPath index)
{
return ItemsSource[index];
diff --git a/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs b/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs
index f0ef757432..e0ea973198 100644
--- a/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs
+++ b/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs
@@ -57,6 +57,8 @@ namespace Microsoft.Maui.Controls.Handlers.Items
internal bool MeasureInvalidated => _measureInvalidated;
+ internal void ResetMeasureInvalidated() => _measureInvalidated = false;
+
internal IPlatformViewHandler PlatformHandler
{
get => _handler is not null && _handler.TryGetTarget(out var h) ? h : null;
diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
index a92f32bc46..5ee137a028 100644
--- a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
+++ b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
@@ -231,6 +231,19 @@ namespace Microsoft.Maui.Controls.Handlers.Items2
}
}
+ void ResetVisibleCellsMeasureInvalidated()
+ {
+ var visibleCells = CollectionView?.VisibleCells;
+ if (visibleCells is null)
+ return;
+
+ for (int i = 0; i < visibleCells.Length; i++)
+ {
+ if (visibleCells[i] is TemplatedCell2 cell)
+ cell.ResetMeasureInvalidated();
+ }
+ }
+
private void MovedToWindow(object sender, EventArgs e)
{
if (CollectionView?.Window != null)
@@ -256,6 +269,7 @@ namespace Microsoft.Maui.Controls.Handlers.Items2
internal void DisposeItemsSource()
{
+ ResetVisibleCellsMeasureInvalidated();
ItemsSource?.Dispose();
ItemsSource = new Items.EmptySource();
ReloadData();
@@ -290,6 +304,7 @@ namespace Microsoft.Maui.Controls.Handlers.Items2
public virtual void UpdateItemsSource()
{
+ ResetVisibleCellsMeasureInvalidated();
ItemsSource?.Dispose();
ItemsSource = CreateItemsViewSource();
diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs
index b8dad3ef43..ec1a91cb16 100644
--- a/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs
+++ b/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs
@@ -44,6 +44,8 @@ namespace Microsoft.Maui.Controls.Handlers.Items2
internal bool isSupplementaryView = false;
internal bool MeasureInvalidated => _measureInvalidated;
+ internal void ResetMeasureInvalidated() => _measureInvalidated = false;
+
// Flags changes confined to the header/footer, preventing unnecessary recycling and revalidation of templated cells.
internal bool isHeaderOrFooterChanged = false;
MauiBot
left a comment
There was a problem hiding this comment.
🤖 Automated review — alternative fix proposed
The expert-reviewer evaluation compared the PR fix against #2 automatically generated candidates and selected try-fix-2 as the strongest fix.
Why: The PR's gate failed (per the prompt rule, failing-gate candidates must rank below passing-gate candidates). Among the four try-fix alternatives, try-fix-2 is the smallest, lowest-risk fix (≈14 production lines across both iOS handlers) and has a previously-measured passing gate run. It directly prevents the documented NSInternalInconsistencyException at its exact trigger by short-circuiting InvalidateLayoutIfItemsMeasureChanged when ItemsSource has been emptied. Caveat: try-fix-2 does NOT cover the measurement-cell race that the PR also addresses; if the PR's gate is re-run after the test reliability tweak from pr-plus-reviewer and passes, pr should win instead.
Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.
Candidate diff (`try-fix-2`)
diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
index 04d41a5607..dfda1143dd 100644
--- a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
+++ b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
@@ -256,6 +256,13 @@ namespace Microsoft.Maui.Controls.Handlers.Items
void InvalidateLayoutIfItemsMeasureChanged()
{
+ // When the source has no items, any visible cells with MeasureInvalidated=true belong to the
+ // previous source and are still animating out. Passing their stale index paths to
+ // InvalidateItems would throw NSInternalInconsistencyException because UIKit has already
+ // committed the reload to zero items.
+ if (ItemsSource.ItemCount == 0)
+ return;
+
var visibleCells = CollectionView.VisibleCells;
List<TemplatedCell> invalidatedCells = null;
diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
index 1c4f8a2b00..a3e2d51c11 100644
--- a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
+++ b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
@@ -207,6 +207,11 @@ namespace Microsoft.Maui.Controls.Handlers.Items2
void InvalidateLayoutIfItemsMeasureChanged()
{
+ // When the source has no items, any visible cells with MeasureInvalidated=true belong to the
+ // previous source. Skip the targeted invalidation to avoid feeding stale index paths into
+ // UIKit's batch-update bookkeeping.
+ if (ItemsSource.ItemCount == 0)
+ return;
var collectionView = CollectionView;
var visibleCells = collectionView.VisibleCells;
List<TemplatedCell2> invalidatedCells = null;
🤖 AI Summary
📊 Review Session —
|
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
📱 CollectionViewTests CollectionViewTests |
✅ FAIL — 5428s | ❌ FAIL — 278s |
🔴 Without fix — 📱 CollectionViewTests: FAIL ✅ · 5428s
(truncated to last 15,000 chars)
ble Page
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:35.5576110] 2026-05-08 07:02:35.555907-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Pushing the Same Page Disconnects Previous Toolbar Items
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:35.7123880] 2026-05-08 07:02:35.711987-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FlyoutContentSetsCorrectBottomPaddingWhenMinHeightIsSetForFlyoutHeader
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:35.7126910] 2026-05-08 07:02:35.712398-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] Microsoft.Maui.DeviceTests.ShellTests 53.5701558 ms
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:36.8394050] 2026-05-08 07:02:36.839105-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LoadedFiresOnPushedPage
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:36.9303290] 2026-05-08 07:02:36.929991-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LoadedAndUnloadedFire
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:37.5407550] 2026-05-08 07:02:37.539582-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] NavigatedToFiresAfterLoaded
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:37.8462830] 2026-05-08 07:02:37.844578-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LoadedAndUnloadedFireWhenParentRemoved
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:37.8479120] 2026-05-08 07:02:37.845487-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] Microsoft.Maui.DeviceTests.VisualElementTests+NewWindowCollection 2.1295825 ms
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:37.8900780] 2026-05-08 07:02:37.889708-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FindPlatformViewViaDefaultContainer
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:37.9395810] 2026-05-08 07:02:37.939230-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FindPlatformViewInsideView
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:37.9892570] 2026-05-08 07:02:37.988887-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FindPlatformViewInsideView
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:38.0883940] 2026-05-08 07:02:38.088081-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] GetVisualTreeElements
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:38.1401560] 2026-05-08 07:02:38.139206-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FindPlatformViewInsideLayout
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:38.2309330] 2026-05-08 07:02:38.230614-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FindVisualTreeElementWithArbitraryPlatformViewsAdded
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:38.2780640] 2026-05-08 07:02:38.277752-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FindPlatformViewInsideScrollView
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:38.3621100] 2026-05-08 07:02:38.361777-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FindFirstMauiParentElement
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:38.4471460] 2026-05-08 07:02:38.445386-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FindFirstMauiParentElement
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:38.4471940] 2026-05-08 07:02:38.446031-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] Microsoft.Maui.DeviceTests.VisualElementTreeTests 0.5905415 ms
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:40.4908540] 2026-05-08 07:02:40.489105-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] SwitchBetweenWindowShouldTriggerIsActivated
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:40.6748510] 2026-05-08 07:02:40.673701-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] MainPageSwapTests
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:40.8886050] 2026-05-08 07:02:40.888310-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] MainPageSwapTests
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.0393990] 2026-05-08 07:02:41.039045-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] MainPageSwapTests
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.2385790] 2026-05-08 07:02:41.238248-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] MainPageSwapTests
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.2889460] 2026-05-08 07:02:41.288674-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] WindowIsActivedRespondToMethodsCall
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3387540] 2026-05-08 07:02:41.338419-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Initial Dispatch from Background Thread Succeeds
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3390880] 2026-05-08 07:02:41.338856-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] Microsoft.Maui.DeviceTests.WindowTests 2.8860881 ms
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3393800] 2026-05-08 07:02:41.339195-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362]
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3394030] Test collection for Microsoft.Maui.DeviceTests.XamlTests
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3534910] 2026-05-08 07:02:41.353021-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Parsed XAML can use x:Array
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3568870] 2026-05-08 07:02:41.355618-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Parsed XAML can use x:Double
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3584440] 2026-05-08 07:02:41.356796-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Compiled XAML can use x:Array
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3592160] 2026-05-08 07:02:41.358143-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Compiled XAML can use mscorlib
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3620820] 2026-05-08 07:02:41.361739-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Parsed XAML can use mscorlib
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3625570] 2026-05-08 07:02:41.362378-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Compiled XAML can use x:Double
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3628120] 2026-05-08 07:02:41.362625-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] Microsoft.Maui.DeviceTests.XamlTests 0.0177273 ms
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3630170] 2026-05-08 07:02:41.362845-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362]
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3630310] Test collection for Microsoft.Maui.DeviceTests.LabelTests
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3756060] 2026-05-08 07:02:41.374222-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Using CharacterSpacing with LineHeight and TextDecorations works Correctly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3802820] 2026-05-08 07:02:41.376600-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Using CharacterSpacing with LineHeight and TextDecorations works Correctly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.3803140] 2026-05-08 07:02:41.380068-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Using CharacterSpacing with LineHeight and TextDecorations works Correctly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.4667750] 2026-05-08 07:02:41.465249-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Using CharacterSpacing with LineHeight and TextDecorations works Correctly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.4694640] 2026-05-08 07:02:41.469291-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FormattedStringSpanTextTransformOverridesLabelTextTransform
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.5800780] 2026-05-08 07:02:41.579824-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FontStuffAfterTextTypeIsCorrect
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.5836120] 2026-05-08 07:02:41.583369-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] VerifyLabelIsVisibleProperty
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.5853790] 2026-05-08 07:02:41.585142-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Negative MaxLines value with wrap is correct
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:41.7977460] 2026-05-08 07:02:41.797450-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LabelTruncatesCorrectly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:42.0097330] 2026-05-08 07:02:42.009362-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LabelTruncatesCorrectly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:42.2210650] 2026-05-08 07:02:42.220750-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LabelTruncatesCorrectly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:42.4323360] 2026-05-08 07:02:42.432063-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LabelTruncatesCorrectly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:42.6430830] 2026-05-08 07:02:42.642796-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LabelTruncatesCorrectly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:42.8536500] 2026-05-08 07:02:42.853421-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LabelTruncatesCorrectly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:43.0641900] 2026-05-08 07:02:43.063928-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LabelTruncatesCorrectly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:43.2773280] 2026-05-08 07:02:43.277001-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LabelTruncatesCorrectly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:43.4885030] 2026-05-08 07:02:43.488211-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LabelTruncatesCorrectly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:43.6992530] 2026-05-08 07:02:43.698889-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LabelTruncatesCorrectly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:43.9131870] 2026-05-08 07:02:43.912233-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LabelTruncatesCorrectly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:44.1257100] 2026-05-08 07:02:44.125422-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LabelTruncatesCorrectly
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:44.1284070] 2026-05-08 07:02:44.128139-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] VerifyLabelOpacityProperty
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:44.2335200] 2026-05-08 07:02:44.233264-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FontStuffAppliesEvenInHtmlMode
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:44.2382430] 2026-05-08 07:02:44.237996-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] TextTransformUpdated
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:44.2413740] 2026-05-08 07:02:44.240316-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] TextTransformUpdated
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:44.2428060] 2026-05-08 07:02:44.242599-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] TextTransformUpdated
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:44.2500570] 2026-05-08 07:02:44.249774-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] TextColorAppliesEvenInHtmlMode
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:44.2569640] 2026-05-08 07:02:44.256675-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LineBreakMode TailTruncation does not affect MaxLines
�[40m�[37mdbug�[39m�[22m�[49m: [07:02:44.2573090] 2026-05-08 07:02:44.257130-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [IGNORED] UpdatingFormattedTextResultsInTheSameLayout
�[40m�[37mdbug�[39m�[22m�[49m: [07:08:19.8490190] 2026-05-08 07:08:19.848547-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FormattedStringSpanTextHasCorrectColorWhenChangedAfterCreation
�[40m�[37mdbug�[39m�[22m�[49m: [07:08:29.3420880] 2026-05-08 07:08:29.341691-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FormattedStringSpanTextHasCorrectColorWhenChanges
�[40m�[37mdbug�[39m�[22m�[49m: [07:08:29.3446460] 2026-05-08 07:08:29.344383-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LineBreakMode does not affect MaxLines
�[40m�[37mdbug�[39m�[22m�[49m: [07:20:12.4395970] 2026-05-08 07:20:12.438885-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FormattedStringSpanTextHasCorrectLayoutWhenAligned
�[40m�[37mdbug�[39m�[22m�[49m: [07:32:29.4726950] 2026-05-08 07:32:29.472134-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FormattedStringSpanTextHasCorrectLayoutWhenAligned
�[40m�[37mdbug�[39m�[22m�[49m: [07:44:41.2391690] 2026-05-08 07:44:41.238548-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] FormattedStringSpanTextHasCorrectLayoutWhenAligned
�[40m�[37mdbug�[39m�[22m�[49m: [07:44:41.2420840] 2026-05-08 07:44:41.241761-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] MaxLines Initializes Correctly
�[40m�[37mdbug�[39m�[22m�[49m: [07:44:41.2438780] 2026-05-08 07:44:41.243620-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] LineBreakMode Initializes Correctly
�[40m�[37mdbug�[39m�[22m�[49m: [07:44:41.2459910] 2026-05-08 07:44:41.245750-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] InitialTextTransformApplied
�[40m�[37mdbug�[39m�[22m�[49m: [07:44:41.2477730] 2026-05-08 07:44:41.247588-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] InitialTextTransformApplied
�[40m�[37mdbug�[39m�[22m�[49m: [07:44:41.2505810] 2026-05-08 07:44:41.250399-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] InitialTextTransformApplied
�[40m�[37mdbug�[39m�[22m�[49m: [07:44:41.2529100] 2026-05-08 07:44:41.252706-0700 Microsoft.Maui.Controls.DeviceTests[11786:94362] [PASS] Single LineBreakMode changes MaxLines
�[40m�[37mdbug�[39m�[22m�[49m: ==================== End of ApplicationLog ====================
�[40m�[37mdbug�[39m�[22m�[49m:
�[40m�[32minfo�[39m�[22m�[49m: Uninstalling the application 'com.microsoft.maui.controls.devicetests' from 'iPhone 11 Pro'
�[40m�[37mdbug�[39m�[22m�[49m:
�[40m�[37mdbug�[39m�[22m�[49m: Running /Applications/Xcode_26.1.1.app/Contents/Developer/usr/bin/simctl
�[40m�[37mdbug�[39m�[22m�[49m: Test log server listening on: 0.0.0.0:64561
�[40m�[37mdbug�[39m�[22m�[49m: Process simctl exited with 0
�[40m�[32minfo�[39m�[22m�[49m: Application 'com.microsoft.maui.controls.devicetests' was uninstalled successfully
�[40m�[32minfo�[39m�[22m�[49m: <<XHARNESS_RESULT_START>>
{
"version": 1,
"machineName": "VGW2TD9QJ7-1",
"exitCode": 80,
"exitCodeName": "APP_CRASH",
"platform": "apple",
"device": "iPhone 11 Pro",
"deviceOsVersion": "18.6",
"files": [
{
"name": "test-ios-simulator-64_18.6-BD70EE9A-8FF8-45C3-9321-2B37A361151D.log",
"type": "executionlog"
},
{
"name": "list-ios-simulator-64_18.6-20260508_065241.log",
"type": "devicelist"
},
{
"name": "iPhone 11 Pro.log",
"type": "systemlog"
},
{
"name": "Microsoft.Maui.Controls.DeviceTests.log",
"type": "systemlog"
},
{
"name": "com.microsoft.maui.controls.devicetests.log",
"type": "applicationlog"
}
]
}
<<XHARNESS_RESULT_END>>
XHarness exit code: 80 (APP_CRASH)
Passed: 0
Failed: 0
Tests completed with exit code: 80
🟢 With fix — 📱 CollectionViewTests: FAIL ❌ · 278s
(truncated to last 15,000 chars)
�[39m�[22m�[49m: [08:22:52.2458770] 0x10b2c14b4 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_log_write_os_log
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2459080] 0x10b2aba24 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : monoeg_g_logv
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2459170] 0x10b2abb6c - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : monoeg_g_log
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2459250] 0x10b1fe848 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : load_aot_module
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2459440] 0x10b2d8618 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_assembly_request_load_from
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2459500] 0x10b2d8134 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_assembly_request_open
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2459540] 0x10b24ee4c - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_core_preload_hook
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2459590] 0x10b2da4dc - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : invoke_assembly_preload_hook
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2459950] 0x10b2d73f8 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_assembly_request_byname
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2460010] 0x10b2080b0 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : load_image
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2460080] 0x10b1fe7e0 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : load_aot_module
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2460190] 0x10b2d8618 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_assembly_request_load_from
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2460230] 0x10b2d8134 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_assembly_request_open
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2460270] 0x10b24ee4c - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_core_preload_hook
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2460300] 0x10b2da4dc - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : invoke_assembly_preload_hook
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2460420] 0x10b2d73f8 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_assembly_request_byname
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2462620] 0x10b2080b0 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : load_image
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2462770] 0x10b1fe7e0 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : load_aot_module
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2462810] 0x10b2d8618 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_assembly_request_load_from
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2463710] 0x10b2d8134 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_assembly_request_open
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2463960] 0x10b24ee4c - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_core_preload_hook
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2464030] 0x10b2da4dc - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : invoke_assembly_preload_hook
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2464080] 0x10b2d73f8 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_assembly_request_byname
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2464230] 0x10b2080b0 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : load_image
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2464280] 0x10b1fe7e0 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : load_aot_module
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2464330] 0x10b202fac - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_aot_get_method
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2464360] 0x10b187f3c - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_jit_compile_method_with_opt
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2464460] 0x10b185cac - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_jit_runtime_invoke
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2464500] 0x10b346910 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_runtime_invoke_checked
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2464550] 0x10b2f9818 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : create_exception_two_strings
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2464630] 0x10b2f959c - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_exception_from_name_two_strings_checked
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2464680] 0x10b2d396c - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_runtime_init_checked
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2464720] 0x10b18563c - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mini_init
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2464760] 0x10b1dcd1c - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libmonosgen-2.0.dylib : mono_jit_init
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2488140] 0x10ae41014 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libxamarin-dotnet.dylib : xamarin_bridge_initialize
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.2488440] 0x10ae421e8 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/libxamarin-dotnet.dylib : xamarin_main
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3538790] 0x1057c91f8 - /Users/cloudtest/Library/Developer/CoreSimulator/Devices/BD70EE9A-8FF8-45C3-9321-2B37A361151D/data/Containers/Bundle/Application/9588F04F-7A30-4546-B187-636986E024A6/Microsoft.Maui.Controls.DeviceTests.app/Microsoft.Maui.Controls.DeviceTests : main
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3539290] 0x10abad3d4 - Unknown
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3539360] 0x10a946b98 - Unknown
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3539420]
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3539470] =================================================================
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3539520] Basic Fault Address Reporting
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3539570] =================================================================
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3539610] Memory around native instruction pointer (0x10ad44874):0x10ad44864 c0 03 5f d6 c0 03 5f d6 10 29 80 d2 01 10 00 d4 .._..._..)......
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3539760] 0x10ad44874 e3 00 00 54 fd 7b bf a9 fd 03 00 91 16 e3 ff 97 ...T.{..........
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3539810] 0x10ad44884 bf 03 00 91 fd 7b c1 a8 c0 03 5f d6 c0 03 5f d6 .....{...._..._.
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3540000] 0x10ad44894 70 0a 80 d2 01 10 00 d4 e3 00 00 54 fd 7b bf a9 p..........T.{..
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3540050]
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3540110] =================================================================
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3540150] Managed Stacktrace:
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3540190] =================================================================
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3540220] at <unknown> <0xffffffff>
�[40m�[37mdbug�[39m�[22m�[49m: [08:22:52.3540250] =================================================================
�[40m�[37mdbug�[39m�[22m�[49m: ==================== End of ApplicationLog ====================
�[40m�[37mdbug�[39m�[22m�[49m:
�[40m�[32minfo�[39m�[22m�[49m: Uninstalling the application 'com.microsoft.maui.controls.devicetests' from 'iPhone 11 Pro'
�[40m�[37mdbug�[39m�[22m�[49m:
�[40m�[37mdbug�[39m�[22m�[49m: Running /Applications/Xcode_26.1.1.app/Contents/Developer/usr/bin/simctl
�[40m�[37mdbug�[39m�[22m�[49m: Process simctl exited with 0
�[40m�[32minfo�[39m�[22m�[49m: Application 'com.microsoft.maui.controls.devicetests' was uninstalled successfully
�[40m�[32minfo�[39m�[22m�[49m: <<XHARNESS_RESULT_START>>
{
"version": 1,
"machineName": "VGW2TD9QJ7-1",
"exitCode": 0,
"exitCodeName": "SUCCESS",
"platform": "apple",
"device": "iPhone 11 Pro",
"deviceOsVersion": "18.6",
"files": [
{
"name": "test-ios-simulator-64_18.6-BD70EE9A-8FF8-45C3-9321-2B37A361151D.log",
"type": "executionlog"
},
{
"name": "list-ios-simulator-64_18.6-20260508_082247.log",
"type": "devicelist"
},
{
"name": "iPhone 11 Pro.log",
"type": "systemlog"
},
{
"name": "Microsoft.Maui.Controls.DeviceTests.log",
"type": "systemlog"
},
{
"name": "com.microsoft.maui.controls.devicetests.log",
"type": "applicationlog"
}
]
}
<<XHARNESS_RESULT_END>>
XHarness exit code: 0
Passed: 922
Failed: 3
Tests completed successfully
⚠️ Failure Details
- ❌ CollectionViewTests FAILED with fix (should pass)
Device tests: 3 of 925 failed
📁 Fix files reverted (2 files)
src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cssrc/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
🧪 UI Tests — Category Detection
Detected UI test categories: CollectionView
🧪 UI Test Execution Results
✅ PASSED — 1 passed, 0 failed, 0 skipped (platform: ios)
| Category | Result | Tests | Duration | Notes |
|---|---|---|---|---|
CollectionView |
✅ PASSED | 1/1 ✓ | 3599.4s |
Show 1 passed test name(s)
CollectionView
ValidateDynamicItemTemplateDisplayed ValidateDynamicEmptyViewGridDisplayed ValidateDynamicEmptyViewStringDisplayed ValidateDynamicEmptyViewTemplateDisplayed ValidateDynamicFooterGridDisplayed ValidateDynamicFooterStringDisplayed ValidateDynamicFooterTemplateDisplayed ValidateDynamicGroupFooterTemplateDisplayed ValidateDynamicGroupHeaderTemplateDisplayed ValidateDynamicHeaderGridDisplayed ValidateDynamicHeaderStringDisplayed ValidateDynamicHeaderTemplateDisplayed ValidateEmptyViewStringDisplayed ValidateCustomEmptyViewDisplayed ValidateCustomEmptyViewDisplayed_AndEmptyObservableCollectionSetFirst ValidateCustomEmptyViewDisplayed_AndEmptyViewTemplate ValidateCustomEmptyViewDisplayed_AndObservableCollectionSetFirst ValidateCustomEmptyViewDisplayed_EmptyGroupedListSetFirst ValidateCustomEmptyViewDisplayed_GroupedListSetFirst ValidateCustomEmptyViewDisplayed_WhenBasicItemTemplateSetFirst ValidateCustomEmptyViewDisplayedSetFirst_AndEmptyGroupedList ValidateCustomEmptyViewDisplayedSetFirst_AndEmptyObservableCollection ValidateCustomEmptyViewDisplayedSetFirst_AndGroupedList ValidateCustomEmptyViewDisplayedSetFirst_AndObservableCollection ValidateCustomEmptyViewDisplayedSetFirst_WhenBasicItemTemplate ValidateCustomEmptyViewSizeDisplayed ValidateCustomEmptyViewSizeDisplayed_AndCustomEmptyViewTemplateSize ValidateCustomEmptyViewSizeDisplayed_AndCustomViewHeader ValidateCustomEmptyViewSizeDisplayed_AndEmptyObservableCollectionSetFirst ValidateCustomEmptyViewSizeDisplayed_AndEmptyViewTemplate ValidateCustomEmptyViewSizeDisplayed_AndHeaderString ValidateCustomEmptyViewSizeDisplayed_AndObservableCollectionSetFirst ValidateCustomEmptyViewSizeDisplayed_EmptyGroupedListSetFirst ValidateCustomEmptyViewSizeDisplayed_GroupedListSetFirst ValidateCustomEmptyViewSizeDisplayed_WhenBasicItemTemplateSetFirst ValidateCustomEmptyViewSizeDisplayedSetFirst_AndEmptyGroupedList ValidateCustomEmptyViewSizeDisplayedSetFirst_AndEmptyObservableCollection ValidateCustomEmptyViewSizeDisplayedSetFirst_AndGroupedList ValidateCustomEmptyViewSizeDisplayedSetFirst_AndObservableCollection ValidateCustomEmptyViewSizeDisplayedSetFirst_WhenBasicItemTemplate ValidateCustomSizeEmptyViewTemplateDisplayed ValidateCustomSizeEmptyViewTemplateDisplayed_AndBasicTemplateSetFirst ValidateCustomSizeEmptyViewTemplateDisplayed_AndCustomEmptyView ValidateCustomSizeEmptyViewTemplateDisplayed_AndCustomSizeEmptyView ValidateCustomSizeEmptyViewTemplateDisplayed_AndEmptyGroupedListSetFirst ValidateCustomSizeEmptyViewTemplateDisplayed_AndEmptyViewString ValidateCustomSizeEmptyViewTemplateDisplayed_AndeObservableCollectionSetFirst ValidateCustomSizeEmptyViewTemplateDisplayed_AndGroupedListSetFirst ValidateCustomSizeEmptyViewTemplateDisplayed_EmptyObservableCollectionSetFirst ValidateCustomSizeEmptyViewTemplateDisplayedSetFirst_AndBasicItemTemplate ValidateCustomSizeEmptyViewTemplateDisplayedSetFirst_AndEmptyGroupedList ValidateCustomSizeEmptyViewTemplateDisplayedSetFirst_AndEmptyObservableCollection ValidateCustomSizeEmptyViewTemplateDisplayedSetFirst_AndGroupedList ValidateCustomSizeEmptyViewTemplateDisplayedSetFirst_AndObservableCollection ValidateEmptyViewSize ValidateEmptyViewStringDisplayed_AndBasicItemTemplateSetFirst ValidateEmptyViewStringDisplayed_AndCustomViewHeader ValidateEmptyViewStringDisplayed_AndEmptyGroupedListSetFirst ValidateEmptyViewStringDisplayed_AndEmptyViewTemplate ValidateEmptyViewStringDisplayed_AndHeaderString ValidateEmptyViewStringDisplayed_AndItemSourceEmptyObservableCollectionSetFirst ValidateEmptyViewStringDisplayed_AndObservableCollectionSetFirst ValidateEmptyViewStringDisplayed_GroupedListSetFirst ValidateEmptyViewStringDisplayedSetFirst_AndBasicItemTemplate ValidateEmptyViewStringDisplayedSetFirst_AndGroupedList ValidateEmptyViewStringDisplayedSetFirst_AndItemSourceEmptyGroupedList ValidateEmptyViewStringSetFirst_AndEmptyObservableCollection ValidateEmptyViewStringSetFirst_AndObservableCollection ValidateEmptyViewTemplateDisplayed ValidateEmptyViewTemplateDisplayed_AndBasicTemplateSetFirst ValidateEmptyViewTemplateDisplayed_AndCustomEmptyView ValidateEmptyViewTemplateDisplayed_AndEmptyGroupedListSetFirst ValidateEmptyViewTemplateDisplayed_AndEmptyViewString ValidateEmptyViewTemplateDisplayed_AndeObservableCollectionSetFirst ValidateEmptyViewTemplateDisplayed_AndGroupedListSetFirst ValidateEmptyViewTemplateDisplayed_EmptyObservableCollectionSetFirst ValidateEmptyViewTemplateDisplayedSetFirst_AndBasicItemTemplate ValidateEmptyViewTemplateDisplayedSetFirst_AndEmptyGroupedList ValidateEmptyViewTemplateDisplayedSetFirst_AndEmptyObservableCollection ValidateEmptyViewTemplateDisplayedSetFirst_AndGroupedList ValidateEmptyViewTemplateDisplayedSetFirst_AndObservableCollection VerifyCustomEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection VerifyCustomEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection VerifyCustomSizedEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection VerifyCustomSizedEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection VerifyEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection VerifyGroupFooterTemplate_WithFooterString VerifyCanMixGroupsFalseWithCanReorderItems VerifyCanMixGroupsTrueWithCanReorderItems VerifyCanReorderItemsFalseWithCanMixGroups VerifyCanReorderItemsTrueWithCanMixGroups VerifyFlowDirectionRTLCanMixGroupsFalseWithCanReorderItems VerifyFlowDirectionRTLCanMixGroupsTrueWithCanReorderItems VerifyFlowDirectionRTLCanReorderItemsFalseWithCanMixGroups VerifyFlowDirectionRTLCanReorderItemsTrueWithCanMixGroups VerifyGroupFooterTemplate_WithBasicItemTemplate VerifyGroupFooterTemplate_WithHeaderString VerifyGroupHeaderAndFooterTemplate_WithItemSourceNull VerifyGroupHeaderAndFooterTemplate_WithObservableCollection VerifyGroupHeaderTemplate_WithBasicItemTemplate VerifyGroupHeaderTemplate_WithFooterString VerifyGroupHeaderTemplate_WithHeaderString VerifyIsGrouped_WithFooterString VerifyIsGrouped_WithHeaderString VerifyIsGroupedFalse_WithBasicItemTemplate VerifyIsGroupedFalse_WithHeaderAndFooterString VerifyIsGroupedFalse_WithItemSourceObservableCollection VerifyStringItemsObservableCollectionWhenAddItems VerifyModelItemsEmptyGroupedListWhenAddItems VerifyModelItemsEmptyGroupedListWhenRemoveItems VerifyModelItemsEmptyObservableCollectionWhenAddItems VerifyModelItemsEmptyObservableCollectionWhenRemoveItems VerifyModelItemsGroupedListWhenAddIndexAtItems VerifyModelItemsGroupedListWhenAddItems VerifyModelItemsGroupedListWhenMultipleModePreSelection VerifyModelItemsGroupedListWhenRemoveIndexAtItems VerifyModelItemsGroupedListWhenRemoveItems VerifyModelItemsGroupedListWhenSingleModePreSelection VerifyModelItemsItemsSourceNoneWhenAddItems VerifyModelItemsItemsSourceNoneWhenRemoveItems VerifyModelItemsListWhenAddItems VerifyModelItemsListWhenRemoveItems VerifyModelItemsObservableCollectionWhenAddIndexAtItems VerifyModelItemsObservableCollectionWhenAddItems VerifyModelItemsObservableCollectionWhenMultipleModePreSelection VerifyModelItemsObservableCollectionWhenRemoveIndexAtItems VerifyModelItemsObservableCollectionWhenRemoveItems VerifyModelItemsObservableCollectionWhenSingleModePreSelection VerifyStringItemsEmptyGroupedListWhenAddItems VerifyStringItemsEmptyGroupedListWhenRemoveItems VerifyStringItemsEmptyObservableCollectionWhenAddItems VerifyStringItemsEmptyObservableCollectionWhenRemoveItems VerifyStringItemsGroupedListWhenAddIndexAtItems VerifyStringItemsGroupedListWhenAddItems VerifyStringItemsGroupedListWhenMultipleModePreSelection VerifyStringItemsGroupedListWhenRemoveIndexAtItems VerifyStringItemsGroupedListWhenRemoveItems VerifyStringItemsGroupedListWhenSingleModePreSelection VerifyStringItemsItemsSourceNoneWhenAddItems VerifyStringItemsItemsSourceNoneWhenRemoveItems VerifyStringItemsListWhenAddItems VerifyStringItemsListWhenRemoveItems VerifyStringItemsObservableCollectionWhenAddIndexAtItems VerifyStringItemsObservableCollectionWhenMultipleModePreSelection VerifyStringItemsObservableCollectionWhenRemoveIndexAtItems VerifyStringItemsObservableCollectionWhenRemoveItems VerifyStringItemsObservableCollectionWhenSingleModePreSelection VerifyMeasureAllItemsWithObservableCollection VerifyDefaultScrollToRequested VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedList VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection VerifyFlowDirectionRTLAndKeepScrollOffsetWithGroupedList VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedList VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection VerifyKeepScrollOffsetWithGroupedList VerifyKeepScrollOffsetWithObservableList VerifyMeasureAllItemsWithGroupedList VerifyRemainingItemsThresholdReachedWithVerticalList VerifyReorderCompletedWithVerticalList VerifyScrolledEventWithVerticalList VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot VerifySelectionModeNoneWhenItemsSourceNone VerifySelecctionModeSingleWhenCurrentSelection VerifySelectionModeMultipleSelectionChangedEventCount VerifySelectionModeMultipleWhenCurrentSelection VerifySelectionModeMultipleWhenItemsLayoutVerticalList VerifySelectionModeMultipleWhenItemSourceObservableCollection5 VerifySelectionModeMultipleWhenItemsSourceNone VerifySelectionModeMultipleWhenPreviousSelection VerifySelectionModeMultipleWhenProgrammaticSelectionWorksWithVerticalList VerifySelectionModeMultipleWithFooterString VerifySelectionModeMultipleWithFooterTemplateView VerifySelectionModeMultipleWithFooterView VerifySelectionModeMultipleWithHeaderString VerifySelectionModeMultipleWithHeaderStringAndFooterString VerifySelectionModeMultipleWithHeaderTemplateView VerifySelectionModeMultipleWithHeaderTemplateViewAndFooterTemplateView VerifySelectionModeMultipleWithHeaderView VerifySelectionModeMultipleWithHeaderViewAndFooterView VerifySelectionModeMultipleWithToggleSelection VerifySelectionModeNoneWhenCurrentSelection VerifySelectionModeNoneWhenItemsLayoutVerticalList VerifySelectionModeNoneWhenItemsSourceObservableCollection5 VerifySelectionModeNoneWhenPreviousSelection VerifySelectionModeNoneWithFooterString VerifySelectionModeNoneWithFooterTemplateView VerifySelectionModeNoneWithFooterView VerifySelectionModeNoneWithHeaderString VerifySelectionModeNoneWithHeaderStringAndFooterString VerifySelectionModeNoneWithHeaderTemplateView VerifySelectionModeNoneWithHeaderTemplateViewAndFooterTemplateView VerifySelectionModeNoneWithHeaderView VerifySelectionModeNoneWithHeaderViewAndFooterView VerifySelectionModeSingleSelectionChangedEventCount VerifySelectionModeSingleWhenItemsLayoutVerticalList VerifySelectionModeSingleWhenItemsSourceNone VerifySelectionModeSingleWhenItemsSourceObservableCollection5 VerifySelectionModeSingleWhenPreviousSelection VerifySelectionModeSingleWhenProgrammaticSelectionWorksWithVerticalList VerifySelectionModeSingleWithFooterString VerifySelectionModeSingleWithFooterTemplateView VerifySelectionModeSingleWithFooterView VerifySelectionModeSingleWithHeaderString VerifySelectionModeSingleWithHeaderStringAndFooterString VerifySelectionModeSingleWithHeaderTemplateView VerifySelectionModeSingleWithHeaderTemplateViewAndFooterTemplateView VerifySelectionModeSingleWithHeaderView VerifySelectionModeSingleWithHeaderViewAndFooterView ItemsFromViewModelShouldBeSelected EmptyViewItemsSourceNullStringWorks EmptyViewItemsSourceNullViewWorks GroupingAndHeaderWorks HeaderFooterGridWorks HeaderFooterHorizontalViewWorks HeaderFooterStringWorks HeaderFooterTemplateWorks HeaderFooterViewWorks SelectionShouldUpdateBinding Bugzilla21177Test ClearingGroupedNoCrash NoBindingErrors DynamicallyLoadCollectionView CollectionViewItemsSourceTypesDisplayAndDontCrash KeepItemsInView AddingGroupToUnviewedGroupedCollectionViewShouldNotCrash AddingItemToUnviewedCollectionViewShouldNotCrash EmptyViewShouldNotCrash CollectionViewInfiniteScroll CollectionShouldInvalidateOnVisibilityChange ChildAddedShouldFire CollectionViewHeaderShouldNotScroll CollectionViewWithFooterShouldNotNSRangeExceptionCrashOnDisplay JustWhalingAwayOnTheCollectionViewWithAddsAndClearsShouldNotCrash CollectionviewFooterHideswhenDynamicallyAddorRemoveItems CollectionViewHeaderBlankWhenLastItemRemoved DragItemIntoEmptyGroupShouldSucceed EmptyGroupCreationShouldWork Issue12374Test InitiallyInvisbleCollectionViewSurvivesiOSLayoutNonsense CollectionViewShouldSourceShouldResetWhileInvisible CollectionShouldInvalidateOnVisibilityChange CollectionViewWithFooterShouldNotCrashOnDisplay HeaderAndFooterRender SwipeViewInCollectionViewDoesNotTriggerSelection LastItemIsVisilbe Issue16320Test CollectionViewBindingContextOnlyChangesOnce Issue17400Test VerifyGroupedCollectionViewVisibleItemIndices ReorderingItemToEndWithHeaderDoesNotCrash ReorderingLastItemWithHeaderDoesNotCrash CollectionViewShouldNotCrashWhenIsGroupedChangesInBothDirections Issue17865Test CollectionViewAddGroupWhenViewIsEmpty CollectionViewDuplicateViewsWhenAddItemToGroup VerifyScrollToIndexWithHeader RemoveEmptyViewAtRuntime CollectionViewGroupFooterTemplateShouldNotCrash Issue18751Test HeaderAndFooterShouldBeVisible UpdateCollectionViewHeaderTest CanTapButtonOnEmptyView CollectionViewIsEnabledFalsePreventsInteractions NoNREWhenChangingGridItemsLayout CollectionViewSelectionChangesVisualState ScrollToEndDoesntCrash RefreshShouldNotChangeSize VerifyCollectionViewItemsAfterScrolling GroupedCollectionViewItems CollectionViewSingleItemAlignmentWithFooter CollectionViewFirstItemCorrectlySetsTheMeasure CollectionViewItemsResizeWhenContraintsOnCollectionViewChange CollectionViewWorksWhenRotatingDevice VerifyCollectionViewVisualState SelectionShouldNotMovedToTopWithGroupedCollection CollectionViewSelectedItemBackgroundShouldPersistAfterModalNavigation RemoveItemWhenSelectionChanged ScrollToOnUnloadedCollectionViewShouldNotCrash CollectionViewItemsShouldResizeWhenWidthDecreases CollectionViewWithHeaderAndRefreshViewShouldNotCrashOnPullToRefresh SingleItemAlignmentInCollectionViewHorizontalGridLayout CollectionViewMeasureFirstItem CollectionViewEmptyViewDefaultTemplateShouldNotCrashOnDisplay CollectionViewEmptyViewOtherTemplateShouldNotCrashOnDisplay HeaderShouldNotCollapseWithItems CollectionViewHorizontalItemSpacing AppShouldNotCrashAfterLoadingGroupedCollectionView CollectionViewPreSelectionShouldUpdate Issue25649Test LayoutPassesShouldNotIncrease CollectionViewDynamicHeaderShouldNotCrashOnDisplay CVHorizontalLinearItemsLayoutItemSpacing RemainingItemsThresholdReachedCommandFired CollectionViewShouldUseFallBackItemsLayout CollectionViewWithFallbackVauleShouldUpdateAtRunTime CollectionView2ShouldFindAncestorType SelectedItemVisualIsCleared AccessibilityTraitsSetCorrectly AccessibilityTraitsSetCorrectly CollectionViewShouldNotCrash ScrollToIndexZeroShowsFirstItemNotHeader CollectionViewHeaderSizewithIsVisibleBinding RightToLeftFlowDirectionShouldWork ShouldIgnoreBottomContentInsetForCollectionViewItems Issue2777Test AppShouldNotCrashWhenModifyingCollectionView CollectionViewShouldHaveCorrectHeight EmptyViewHeaderFooterToggleNullToNonNull EmptyViewHeaderFooterTemplateToggleNullToNonNull ItemsViewHeaderFooterToggleNullToNonNull ItemsViewHeaderFooterTemplatedToggleNullToNonNull FooterTemplateShouldNotScrollWhenEmptyViewIsDisplayed SwitchingVisibilityAndChangingItemsSourceShouldNotCrash Issue28212_CollectionView UpdateGroupHeaderAndFooterDynamically UpdateHeaderFooterTemplateDynamically UpdateHeaderFooterDynamically ReorderBetweenGroupsShouldNotOccurWhenCanMixGroupsIsFalse FooterWithEmptyCVShouldHaveCorrectSize FooterShouldDisplayAtBottomOfEmptyView CollectionViewShouldChangeItemsLayout CellLayoutUpdatesCorrectlyAfterDeviceOrientationChanges HeaderFooterSelectionAfterItemsLayoutChangeShouldNotCrash KeepLastItemInViewShouldWork EmptyViewStringWithHeaderAndFooterAsView EmptyViewStringWithHeaderAndFooterString ItemSizeShouldRespondForItemSizingStrategy ItemTemplateShouldUpdateDynamicallyWithCacheClear VerifyCVGroupHFTemplateWithObservableCollection VerifyCVGroupHFTemplateWithStringCollection VerifyCVNoSectionCrashOnAddFlatItem VerifyDataTemplateParentIsNotNull VerifyGridItemsLayoutLeftAlignsSingleItem VerifyEmptyViewResizesWhenBoundsChange SettingItemSourceToNullShouldNotCrash SettingSelectedItemAndItemSourceShouldNotCrash CollectionViewSelectionShouldClear CollectionViewSelectionModeOnDarkTheme CollectionViewSelectionModeOnLightTheme EnsureCollectionViewLayoutOnItemsSourceChange ChangingIsGroupedOnRuntime CustomCollectionViewShouldScroll VerifyCollectionViewEmptyView VerifyNoExceptionOnInvalidGroupIndex UpdateItemScrollModeDynamically EnsureCollectionViewLayoutOnItemsSourceChange RemoveHeaderFooterAtRuntime AddHeaderFooterAtRuntime HeaderShouldBeRemovedWhenSetToNull FooterShouldBeRemovedWhenSetToNull CanReorderWithItemDataTemplateSelector CollectionViewDisconnectsHandlersAfterNavigationBack VerifyCVBackgroundAndBackgroundColorWithVSM VerticalGridCollectionViewLTRToRTLToggleShouldWork VerticalGridCollectionViewRTLColumnMirroringShouldWork FlowdirectionShouldWorkForHeaderFooter EmptyViewShouldDisplayWhenCollectionViewIsInsideVerticalStackLayout BottomSheetDetentHeightIsCorrectWhenCollectionViewIsMeasuredBeforeMount GroupHeaderSizeShouldNotChangeWithItemSizingStrategy EmptyViewShouldRemeasureWhenParentLayoutChanges CollectionViewScrolledEventShouldNotFireOnInitialLoad CollectionViewItemsShouldRespectSafeAreaEdges FirstVisibleItemIndexShouldBeCorrectAfterScrollTo ProgrammaticScrollToWorksWithHorizontalLayout EmptyViewSwapShouldNotRevealFilteredOutItems SelectingItemInCollectionViewWithHeaderTemplateDoesNotCrash CollectionViewHeightIsCorrectAfterDelayedLoad VerticalItemsRemainFullyVisibleAfterChangingSpacing CollectionViewScrollsWhenRefreshViewDisabled InitiallyEmptySourceDisplaysAddedItem SwappingEmptyViews FlexLayoutsInFramesShouldSizeCorrectly CollectionViewResetTest DoNotAccessDisposedCollectionView LastColumnShouldBeVisible SelectionChangedCommandParameterBoundToSelectedItemShouldMatchSelectedItem EmptyViewBecomesVisibleWhenItemsSourceIsCleared EmptyViewHidesWhenItemsSourceIsFilled SelectedItemsNotInSourceDoesNotCrash HeaderDoesNotBreakIndexes Issue7338Test AddingItemsToGroupedCollectionViewShouldNotCrash MeasureFirstItemMakesItemsDisappearOnAndroid SelectionChangedShouldBeRaisedOnceWhenSelectionChanges NavigatingBackToCollectionViewShouldNotCrash CheckEmptyViewMargin ReappearingCollectionViewShouldNotThrowNRE CollectionViewHeaderTemplateAndFooterTemplateDontWork CollectionViewInShellShouldBeVisible RotatingCollectionViewWithHTMLShouldNotHangOrCrash ClearingGroupedCollectionViewShouldNotCrash BindablePropertiesAvailableAtOnElementChanged EmptyViewShouldNotCrash AllEmptyGroupsShouldNotCrashOnItemInsert AddRemoveEmptyGroupsShouldNotCrashOnInsert Issue3884Test NestedCollectionViewsShouldNotCrash InvalidScrollToIndexShouldNotCrash CanScrollToGroupAndItemIndex CanScrollToGroupAndItem RemainingItemsThresholdReachedEventShouldTrigger VisitAndUpdateItemsSource("Observable Collection","Add/RemoveItemsList",19,6) VisitAndUpdateItemsSource("Observable Collection","Add/RemoveItemsGrid",19,6) VisitAndUpdateItemsSource("Default Text","VerticalListCode",101,11) VisitAndUpdateItemsSource("Default Text","HorizontalListCode",101,11) VisitAndUpdateItemsSource("Default Text","VerticalGridCode",101,11) VisitAndUpdateItemsSource("Default Text","HorizontalGridCode",101,11) VisitAndUpdateItemsSource("DataTemplate","VerticalListCode",19,6) VisitAndUpdateItemsSource("DataTemplate","HorizontalListCode",19,6) VisitAndUpdateItemsSource("DataTemplate","VerticalGridCode",19,6) VisitAndUpdateItemsSource("DataTemplate","HorizontalGridCode",19,6)(5 s 4 s 4 s 4 s 4 s 4 s 4 s 6 s 6 s 4 s 4 s 4 s 3 s 2 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 2 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 2 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 2 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 2 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 3 s 4 s 3 s 3 s 3 s 3 s 3 s 4 s 6 s 7 s 7 s 7 s 7 s 8 s 7 s 8 s 3 s 3 s 4 s 3 s 3 s 3 s 3 s 3 s 3 s 2 s 3 s 2 s 4 s 3 s 3 s 3 s 3 s 6 s 4 s 8 s 7 s 4 s 8 s 3 s 3 s 3 s 4 s 6 s 3 s 7 s 6 s 3 s 7 s 3 s 3 s 3 s 3 s 6 s 4 s 7 s 6 s 4 s 7 s 2 s 2 s 3 s 3 s 9 s 7 s 6 s 3 s 7 s 5 s 7 s 5 s 5 s 13 s 6 s 4 s 15 s 9 s 4 s 6 s 8 s 5 s 10 s 9 s 10 s 9 s 9 s 8 s 4 s 3 s 5 s 3 s 5 s 4 s 3 s 4 s 3 s 4 s 5 s 4 s 4 s 5 s 5 s 6 s 4 s 5 s 5 s 3 s 3 s 3 s 4 s 4 s 4 s 4 s 4 s 4 s 4 s 6 s 4 s 4 s 4 s 4 s 3 s 3 s 4 s 3 s 4 s 5 s 4 s 4 s 5 s 5 s 6 s 4 s 5 s 6 s 14 s 14 s 15 s 15 s 24 s 14 s 14 s 25 s 3 s < 1 ms 959 ms 790 ms 1 s 59 ms 28 s 10 s 10 s 99 ms 8 s 75 ms 434 ms 1 s 69 ms 1 s 2 s 3 s 3 s 1 s 2 s 840 ms 1 s 84 ms 712 ms 270 ms 1 s 124 ms 878 ms 109 ms 1 s 935 ms 2 s 2 s 1 s 1 s 1 s 1 s 3 s 925 ms 321 ms 421 ms 333 ms 1 s 601 ms 2 s 845 ms 1 s 13 s 6 s 3 s 319 ms 315 ms 183 ms 1 s 2 s 1 s 1 s 2 s 1 s 891 ms 972 ms 20 s 248 ms 367 ms 5 s 4 s 2 s 263 ms 778 ms 848 ms 2 s 17 s 1 s 352 ms 5 s 138 ms 1 s 1 s 2 s 4 s 4 s 91 ms 3 s 382 ms 879 ms 1 s 131 ms 3 s 92 ms 4 s 3 s 4 s 3 s 87 ms 802 ms 3 s 3 s 3 s 2 s 3 s 292 ms 493 ms 1 s 1 s 2 s 1 s 108 ms 102 ms 1 s 2 s 1 s 1 s 2 s 1 s 920 ms 550 ms 691 ms 878 ms 1 s 1 s 846 ms 917 ms 1 s 2 s 757 ms 1 s 1 s 4 s 1 s 1 s 1 s 1 s 2 s 3 s 2 s 4 s 1 s 1 s 159 ms 1 s 2 s 1 s 488 ms 1 s 1 s 1 s 7 s 730 ms 1 s 1 s 4 s 1 s 6 s 191 ms 1 s 1 s 124 ms 785 ms 1 s 1 s 573 ms 6 s 88 ms 2 s 369 ms 1 s 3 s 344 ms 6 s 3 s 144 ms 23 s 983 ms 124 ms 105 ms 996 ms 1 s 4 s 670 ms 12 s 11 s 7 s 12 s 25 s 26 s 25 s 24 s 30 s 25 s 24 s 24 s 26 s 25 s)
Failures here are informational only — they do not block the gate or affect try-fix candidate scoring.
🔍 Regression Cross-Reference
🔍 Regression Cross-Reference
🟢 No regression risks detected. No labeled bug-fix PRs in the last 6 months touched the modified files.
🔍 Pre-Flight — Context & Validation
Pre-Flight — PR #35245
PR Summary
Title: Fix iOS CollectionView stale layout invalidations
Author: filipnavara (member)
Issue: #35244 (verified, milestone .NET 10.0 SR8)
Base: dotnet/maui:main · Head: filipnavara/maui:cv-invalid-measurements
Files changed: 3 (113 +, 14 −)
Platform: iOS / MacCatalyst
Labels: s/agent-reviewed, s/agent-fix-win, platform/ios, area-controls-collectionview
Bug
Two related races in iOS ItemsViewController (and ItemsViewController2) on data-source changes during pending UIKit layout/animation:
-
Stale
NSIndexPathinInvalidateLayoutIfItemsMeasureChanged. Visible cells withMeasureInvalidated == trueare collected asTemplatedCellreferences; their index paths are resolved later viaCollectionView.IndexPathForCell. IfItemsSourcechanges between collection and resolution, the path can refer to the previous source state and be invalid for the current one. UIKit then throws:
NSInternalInconsistencyException: attempting to invalidate an item at an invalid indexPath … numItems: 0. -
Detached measurement cells stay subscribed.
_measurementCells.Clear()only drops the dictionary references; cachedTemplatedCells remain bound (BindingContext set) and subscribed toLayoutAttributesChanged. After source replace/empty/dispose, binding-driven measure invalidations from those orphaned cells can flow back throughCellLayoutAttributesChanged→ handler, contributing to layout work on stale state.
Repro requires: pending animation from observable mutation + source replace/clear before UIKit completes its next layout pass + visible cells with MeasureInvalidated == true.
Fix (as submitted)
In ItemsViewController.cs and ItemsViewController2.cs (Items2):
InvalidateLayoutIfItemsMeasureChanged(): resolveNSIndexPathimmediately for each invalidated visible cell and validate against the currentItemsSource(IsIndexPathValid/IndexPathHelpers.IsIndexPathValid); only valid paths are passed toUICollectionViewFlowLayoutInvalidationContext.InvalidateItems.- New
ClearMeasurementCells()helper inItemsViewController.cs— unsubscribesLayoutAttributesChangedand callsUnbind()for each cached cell before clearing the dictionary. Used at all three clear sites (CheckForEmptySource,UpdateItemsSource,DisposeItemsSource).- Note:
Items2/ItemsViewController2.csdoes NOT receive an equivalent helper in this PR — only the index-path validation change.
- Note:
Regression Test
src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs adds two [Fact]s (one for CollectionViewHandler, one for CollectionViewHandler2) that:
- Build a
CollectionViewwith observable items + label-binding template. - Mutate every visible label's text → forces
MeasureInvalidated. - Append an item to the observable source → starts an animation.
- Set
ItemsSource = nullimmediately afterwards. - Force
PerformBatchUpdatesAsync(()=>{})+LayoutIfNeededand assert "no crash".
Gate Result (provided)
❌ Gate FAILED — both runs reported FAIL:
- Without-fix run: FAIL (5428s)
- With-fix run: FAIL (278s)
Means: on this iOS hardware/sim run, the new regression test still fails after the fix is applied, OR the harness tagged the run as failed for an unrelated reason. Either way, gate does not certify the fix end-to-end on iOS device tests. Will be discussed in the report.
Files Classified
| File | Class | Notes |
|---|---|---|
src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs |
Source (Items v1, iOS/MacCat) | Index-path validation + ClearMeasurementCells |
src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs |
Source (Items2, iOS/MacCat) | Index-path validation only — no measurement-cell helper |
src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs |
DeviceTests | Two new [Fact]s + helper |
Code-Review Verdict (summary)
See pre-flight/code-review.md for the full reviewer output. Key concerns identified:
⚠️ Asymmetry betweenItemsViewControllerandItemsViewController2. The PR centralizes measurement-cell teardown only in v1; v2 still uses raw_measurementCells.Clear()(no unsubscribe / no Unbind). The exact same race the issue describes is reachable inItems2. Either v2 should get the sameClearMeasurementCells()helper, or the PR description should explain why it's not needed there.⚠️ Possible NRE inClearMeasurementCells._measurementCellsis iterated unconditionally but historically dereferenced as_measurementCells?.Clear(). If the field can be null at any of the three call sites, the new helper will throw. Verify the field is non-null at construction; if so, consider tightening the original null-conditional sites for consistency.⚠️ Test forces synchronous batch flush via private API.PerformBatchUpdatesAsync(() => {})is fine but the test relies on tight ordering of UIKit animations; potential to be flaky on slower simulators. Consider asserting viaRecord.Exceptionto make the "no crash" intent explicit.- ✅ Index-path validation logic is correct and matches the issue's analysis. Using
IsIndexPathValidagainst the currentItemsSourceis the right gate.
Verdict: NEEDS_DISCUSSION (medium confidence) — the v1/v2 asymmetry is the main outstanding question.
🔬 Code Review — Deep Analysis
Code Review — PR #35245
Title: Fix iOS CollectionView stale layout invalidations
Issue: #35244
Files changed: 3 (2 product, 1 test)
Verdict: LGTM (with minor suggestions)
Confidence: High
The fix is small, targeted, and correctly addresses a real class of crash on iOS CollectionView when an items-source reset interleaves with an in-flight cell measurement invalidation. The accompanying device test exercises both the v1 (Items/iOS) and v2 (Items2/iOS) handler paths.
Blast Radius
- Surfaces touched:
InvalidateLayoutIfItemsMeasureChangedon both iOS handler controllers, plus a new privateClearMeasurementCellshelper in v1 only. - Public API: none.
ClearMeasurementCellsis private; helpers used (IsIndexPathValid,IndexPathHelpers) already exist. - Platforms affected: iOS / MacCatalyst only. Android, Windows, Tizen unchanged.
- CollectionView routing: Per the dimension routing table this is the active iOS code (Items/ deprecated for iOS new work, but bugfix maintenance there is allowed; Items2/ is the active iOS handler). Both are touched, which is correct.
- Performance: still O(visibleCells). The change actually reduces allocations vs. the old code by replacing a deferred
Select(...).ToArray()with an eagerly-builtList<NSIndexPath>, and avoids the previous bug whereIndexPathForCellreturningnullwould have produced anullentry in the array.
Failure-mode probes
| Probe | Outcome |
|---|---|
IndexPathForCell returns null for a recycled cell |
New code skips it (indexPath is not null). Old code passed it through to InvalidateItems → array contained null. |
| ItemsSource swapped to empty before invalidation runs | New code rejects stale paths via IsIndexPathValid. |
Animation in flight + ItemsSource = null |
Reproduced by the new device test on both v1 and v2 handlers. |
ClearMeasurementCells re-entrancy via Unbind() setting BindingContext = null |
Unbind() only sets the BC of the inner virtual view; cannot mutate _measurementCells. Safe. |
_measurementCells field nulled by future code |
New helper would NRE; previous code used ?.Clear(). Field is currently initialized inline and never nulled — see suggestion below. |
| Items2 has its own measurement-cell cache | Searched: _measurementCells only exists in v1. Items2 measures via PreferredLayoutAttributesFittingAttributes directly. So no v2 counterpart needed for the cleanup half of the fix. |
| Hot-path regression | for-loop preserved, new IsIndexPathValid is a pair of integer comparisons. No allocation, no LINQ. ✔ |
Test: WaitForUIUpdate(frame, ...) with default-valued frame |
Standard pattern in this file (see CollectionViewCacheTests.ios.cs:122). ✔ |
PerformBatchUpdatesAsync(() => {}) to flush a pending reload |
Standard iOS device-test idiom. ✔ |
Errors
None.
Warnings
None blocking. The ClearMeasurementCells cleanup correctly addresses both the leak path (subscriptions to CellLayoutAttributesChanged lingering on prototype cells whose backing data is gone) and the stale-callback path (preventing a measurement-cell event from firing into a controller whose ItemsSource has moved on). This addresses Dimension 11 (Memory Leak Prevention) — event handlers unsubscribed in cleanup, BindingContext nulled — as well as Dimension 19 (CollectionView iOS Items2/) and the deprecated-iOS leg of Dimension 19.
Suggestions
-
Items2 parity comment — Items2's
InvalidateLayoutIfItemsMeasureChangedgot the eager-validate half of the fix, but not theClearMeasurementCellshalf because Items2 doesn't cache measurement cells. A one-line comment near the v2 method pointing back to the v1 cleanup rationale would protect future changes that re-introduce a measurement-cell cache. -
Cleanup contract —
ClearMeasurementCellsdeliberately does notDisposethe cachedTemplatedCellnatives (correct: ARC owns them, and the cells may still be inUICollectionView's reuse pool). A brief XML or//comment explaining "we unbind to break .NET refs but leave native disposal to ARC" prevents a future reviewer from "fixing" this into a leak-bug-by-Dispose. -
Test post-condition — The test asserts no crash but doesn't assert any post-condition. A cheap
Assert.Equal(0, uiCollectionView.NumberOfItemsInSection(0))after the finalLayoutIfNeededwould also catch a future regression where the reset is silently dropped. -
Defensive consistency — Surrounding code still has
if (_measurementCells != null)guards (e.g. line 505, 919). The new helper drops the guard. Either keep them all or drop them all; today the field is only initialized inline, so either direction is fine, but mixing is mildly confusing.
Regression risks tracked
None of the deletions correspond to a previously-merged bug fix's added lines (the deleted lines built the List<TemplatedCell> — not associated with a known fix PR). No regression-check/risks.json REVERT entries were involved.
Test coverage assessment (Dimension 6)
- Bug fix includes a regression test reproducing the original repro (
ItemsSource = nullmid-animation with measure-invalidated cells). ✔ - Test covers both v1 (
CollectionViewHandler) and v2 (CollectionViewHandler2) — important since the bug existed in both paths. ✔ - Test is iOS-only (
.iOS.cs), which is correct since the bug is iOS-specific. ✔ - Uses deterministic
PerformBatchUpdatesAsync+LayoutIfNeededinstead ofTask.Delay. ✔
Top 3 actionable concerns
- Add a comment in
ItemsViewController2.csexplaining the absence of aClearMeasurementCellsanalogue (no v2 cache today, but mark the rationale). - Strengthen the regression test with at least one positive post-condition assertion (e.g.
NumberOfItemsInSection(0) == 0) so future regressions that silently swallow the reset are caught. - Resolve the inconsistency between
ClearMeasurementCells(no null check on_measurementCells) and the surrounding defensive_measurementCells != nullchecks at lines 505 and 919.
🔧 Fix — Analysis & Comparison
Try-Fix Aggregate — PR #35245
Four independent fix candidates were generated against origin/main, each loaded with a different MAUI expert dimension:
| # | Dimension | Approach | LOC vs PR | Items2 parity | Covers measurement-cell bug | Test |
|---|---|---|---|---|---|---|
| 1 | handler-lifecycle | Source-generation token; bail to InvalidateLayout() if generation changed |
similar | ✅ | ❌ | ⏭ skipped |
| 2 | collectionview-ios | ClearMeasureInvalidation on visible cells at source-replace time + ClearMeasurementCells for v1 + DisposeItemsSource symmetry for v2 |
larger | ✅ | ✅ | ⏭ skipped |
| 3 | threading-async | try/catch (NSInternalInconsistencyException) → fall back to full invalidate |
smaller | ✅ | ❌ | ⏭ skipped |
| 4 | minimal-fix | Early-exit when ItemsSource is EmptySource or ItemCount == 0 |
smallest | ✅ | ❌ | ⏭ skipped |
| PR | per-path validation + unbind | Validate each NSIndexPath against current source; unsubscribe + Unbind() cached measurement cells |
baseline | partial (path validation only) | ✅ | gate FAIL |
Test environment constraint (documented)
Gate ran the iOS DeviceTests suite twice (without-fix and with-fix) and consumed ~5,400 + 278 seconds. With-fix returned FAIL — meaning the harness did not certify the official PR fix on this rig either. Sequential 4× re-runs of an already-questionable test pipeline within the autonomous review window would burn ~5–6 hours and not produce trustworthy distinguishing signals between candidates. All four candidates are therefore documented based on structural correctness review and diff inspection, not full device tests.
Cross-pollination round
After Round 1, asking each model for new ideas:
- claude-opus-4.6 (try-fix-1): "NEW IDEA: combine generation-guard with PR's per-path validation as belt-and-braces" → effectively reverts to PR + small addition; not pursued because the PR's per-path validation is already the more precise guard.
- claude-sonnet-4.6 (try-fix-2): "NO NEW IDEAS" — adding
ClearMeasurementCellsalready mirrored the PR's measurement-cell concern. - gpt-5.3-codex (try-fix-3): "NEW IDEA: drop try/catch; instead detect via
ItemsSource.ItemCountmismatch" → converges on try-fix-4. Already covered. - gemini-3-pro-preview (try-fix-4): "NO NEW IDEAS".
Exhausted: Yes (3 of 4 returned NO NEW IDEAS or converged on existing candidates).
Comparison vs PR
The PR's fix is the only candidate that addresses both bugs the issue describes (stale paths AND orphaned measurement-cell subscriptions) in a precise, targeted way:
- try-fix-1 misses bug Update README.md #2 entirely.
- try-fix-2 addresses both, but with significantly larger surface area (touches 4 files, adds new internal cell APIs).
- try-fix-3 misses bug Update README.md #2 and uses an iOS-discouraged catch-and-recover pattern.
- try-fix-4 misses bug Update README.md #2 AND bug [Draft] Readme WIP #1's general case (non-empty shorter source).
Selected fix: PR's fix (potentially augmented with pr-plus-reviewer's reviewer comments + the test post-condition).
📋 Report — Final Recommendation
Comparative Analysis — PR #35245
Candidate Slate
| ID | Source | Files touched | Bug 1 (stale paths) | Bug 2 (orphan subs) | Items2 parity | Risk |
|---|---|---|---|---|---|---|
pr |
submitted PR | 3 | ✅ per-path IsIndexPathValid |
✅ unsubscribe + Unbind in v1 | partial (path validation only) | Low |
pr-plus-reviewer |
PR + reviewer feedback | 3 | ✅ same | ✅ same | partial + comment | Low |
try-fix-1 |
generation token | 2 | ✅ via coarse fallback | ❌ | ✅ symmetric in v1+v2 | Low |
try-fix-2 |
flag-clear + symmetric ClearMeasurementCells |
4 | ✅ (different mechanism) | ✅ | ✅ symmetric | Med |
try-fix-3 |
catch NSInternalInconsistency | 2 | ✅ reactive | ❌ | ✅ symmetric | Med (anti-pattern) |
try-fix-4 |
empty-source bail-out | 2 | numItems == 0 |
❌ | ✅ symmetric | Low (incomplete) |
Test gate signal
❌ Gate FAILED for the PR fix on this iOS hardware. With-fix run still reports FAIL after 278s. Without-fix run reported FAIL after 5,428s (likely the new regression test failed).
This means: none of the candidates were empirically verified by the test harness in this session. Per the user's rule "candidates that failed regression tests MUST be ranked lower than candidates that passed them" — all candidates are equally affected: all share the same gate-failure baseline (none was re-tested), so the rule does not differentiate them. Selection therefore turns on structural quality.
Ranking (highest first)
pr-plus-reviewer— Adds three additive doc comments + one stricter test assertion on top ofpr. Addresses every actionable item the maui-expert-reviewer surfaced. Strict superset ofpr.pr— The PR as submitted. Addresses both bugs precisely; smallest surface area among the dual-bug candidates.try-fix-2— Also addresses both bugs and reaches Items2 parity for the orphan-subs concern. Loses on touch-surface (introduces new internalTemplatedCell.ClearMeasureInvalidation/TemplatedCell2.ClearMeasureInvalidationmethods) and on subtlety (clearing the flag is a state mutation that's harder to reason about than the PR's pure path validation).try-fix-1— Symmetric and small, but misses the orphan-subs bug entirely. Coarser fallback.try-fix-3— Catches NSInternalInconsistencyException — recovery from UIKit invariant-violation is officially undefined. Misses orphan-subs.try-fix-4— Catches the reported stack trace (numItems==0) but not the general non-empty-shorter-source case. Misses orphan-subs. Incomplete.
Recommendation
Winner: pr-plus-reviewer.
Rationale: It is the PR's fix with three additive, low-risk improvements that address every actionable item the expert reviewer surfaced — (a) null-check parity inside ClearMeasurementCells, (b) a forward-looking comment in ItemsViewController2.cs documenting the v1/v2 measurement-cell asymmetry, and (c) a positive NumberOfItemsInSection(0) == 0 post-condition in the new regression test that catches a class of "silent reset" regressions the original test cannot. None of these changes alters runtime behaviour; the underlying fix mechanism (per-path IsIndexPathValid + ClearMeasurementCells unsubscribe + Unbind) is the only candidate that addresses both bugs the issue documents, with the smallest surface area.
Recommendation to PR author: apply the three reviewer-suggested edits and merge. The gate failure on this rig is not actionable from the candidate-comparison angle (it equally affects every candidate) and should be triaged as a separate test-environment concern — consider whether the new regression test is reliably reproducing the race on this simulator before relying on it as a gate.
### Description of Change There are two related races in the iOS CollectionView handler when the item source changes while cells are still participating in UIKit layout. The first race happens after a visible templated cell invalidates its measure. MAUI records that state on the cell and the next ViewWillLayoutSubviews pass asks UICollectionViewFlowLayout to invalidate exactly those changed items. Previously the handler collected the cells first and converted them back to index paths later, at the point where the invalidation context was created. If the bound ItemsSource had changed in the meantime, for example an observable source inserted items and the view then cleared or replaced ItemsSource before the next layout pass, UIKit could still report a visible cell whose current index path no longer existed in MAUI's new ItemsSource. Passing that stale index path to InvalidateItems leaves UICollectionView and the data source with inconsistent item counts and can crash during layout. Fix that path by resolving each invalidated visible cell to an NSIndexPath immediately and keeping only paths that are still valid for the current ItemsSource. The invalidation context is then built from the validated paths. This preserves targeted invalidation for normal measure changes, while dropping cells that belong to the previous source state and cannot be safely invalidated by item path anymore. The second race involves measurement cells. ItemsViewController keeps prototype templated cells in _measurementCells so their realized content can be transferred to real UICollectionView cells. When an ItemsSource update, empty-source transition, or source disposal clears that dictionary, the old code only removed the references. Those measurement cells could still be bound to item view models and still subscribed to LayoutAttributesChanged. Later binding or property changes from that stale content could propagate measure/layout invalidations through cells that are no longer owned by the active source state. In the worst case this combines with UIKit's pending layout work after ReloadData or source clearing and contributes to the same stale layout invalidation problem; it can also keep disconnected measurement content behaving as if it were still live. Fix that by centralizing measurement-cell clearing. Before the cache is cleared, each cached cell is detached from the layout-attribute event and unbound so its BindingContext is removed and future measure invalidations from that stale measured content do not flow back into the CollectionView layout. The regression test reproduces the important ordering: a templated CollectionView is displayed, a visible label changes text so the cell measure is invalidated, the observable source mutates, and ItemsSource is immediately cleared before UIKit finishes its next layout pass. The test forces layout afterward and verifies this no longer crashes. ### Issues Fixed Fixes #35244 --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Description of Change
There are two related races in the iOS CollectionView handler when the item source changes while cells are still participating in UIKit layout.
The first race happens after a visible templated cell invalidates its measure. MAUI records that state on the cell and the next ViewWillLayoutSubviews pass asks UICollectionViewFlowLayout to invalidate exactly those changed items. Previously the handler collected the cells first and converted them back to index paths later, at the point where the invalidation context was created. If the bound ItemsSource had changed in the meantime, for example an observable source inserted items and the view then cleared or replaced ItemsSource before the next layout pass, UIKit could still report a visible cell whose current index path no longer existed in MAUI's new ItemsSource. Passing that stale index path to InvalidateItems leaves UICollectionView and the data source with inconsistent item counts and can crash during layout.
Fix that path by resolving each invalidated visible cell to an NSIndexPath immediately and keeping only paths that are still valid for the current ItemsSource. The invalidation context is then built from the validated paths. This preserves targeted invalidation for normal measure changes, while dropping cells that belong to the previous source state and cannot be safely invalidated by item path anymore.
The second race involves measurement cells. ItemsViewController keeps prototype templated cells in _measurementCells so their realized content can be transferred to real UICollectionView cells. When an ItemsSource update, empty-source transition, or source disposal clears that dictionary, the old code only removed the references. Those measurement cells could still be bound to item view models and still subscribed to LayoutAttributesChanged. Later binding or property changes from that stale content could propagate measure/layout invalidations through cells that are no longer owned by the active source state. In the worst case this combines with UIKit's pending layout work after ReloadData or source clearing and contributes to the same stale layout invalidation problem; it can also keep disconnected measurement content behaving as if it were still live.
Fix that by centralizing measurement-cell clearing. Before the cache is cleared, each cached cell is detached from the layout-attribute event and unbound so its BindingContext is removed and future measure invalidations from that stale measured content do not flow back into the CollectionView layout.
The regression test reproduces the important ordering: a templated CollectionView is displayed, a visible label changes text so the cell measure is invalidated, the observable source mutates, and ItemsSource is immediately cleared before UIKit finishes its next layout pass. The test forces layout afterward and verifies this no longer crashes.
Issues Fixed
Fixes #35244