Skip to content

[iOS/MacCatalyst] Fix CollectionView cell misalignment regression on candidate branch#34667

Merged
kubaflo merged 8 commits intoinflight/candidatefrom
fix-34635
Mar 31, 2026
Merged

[iOS/MacCatalyst] Fix CollectionView cell misalignment regression on candidate branch#34667
kubaflo merged 8 commits intoinflight/candidatefrom
fix-34635

Conversation

@praveenkumarkarunanithi
Copy link
Copy Markdown
Contributor

@praveenkumarkarunanithi praveenkumarkarunanithi commented Mar 26, 2026

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue.
Thank you!

Reverts the iOS-specific Window.SafeAreaInsets changes that caused CollectionView cell misalignment on the candidate branch from #33908 , and re-implements safe area handling via per-cell geometric overlap computation that works correctly across all platforms.

Root Cause

Cause of regression:
PR #33908 introduced the issue by using Window.SafeAreaInsets for CollectionView cells; on macOS this includes a 41px title bar. Due to a timing race (IsParentHandlingSafeArea() returning false), all cells incorrectly applied this inset, causing misalignment.
Reverted the iOS changes from PR #33908 in MauiView.cs, restoring the previous behavior. Android changes were retained as they function correctly.

Actual root cause of Issue33604:
PR #33908 modified MauiView.GetAdjustedSafeAreaInsets() to use Window.SafeAreaInsets as the base safe area for CollectionView cell descendants. On macOS, Window.SafeAreaInsets.Top is 41px (title bar). Combined with IsParentHandlingSafeArea() returning false due to a timing race (parent not yet validated), every CollectionView cell received _appliesSafeAreaAdjustments = true with a 41px top inset, causing all items to be misaligned on MacCatalyst.
Fix: Fully reverted all iOS changes from PR #33908 in MauiView.cs, restoring it to the pre-#33908 state. The Android changes from PR #33908 are retained as they work correctly (WindowInsetsCompat propagates to all views regardless of RecyclerView containment).

Description of Change

The fix introduces a shared helper in MauiView (ApplyCellSafeAreaOverride / ComputeCellSafeAreaInsets) used by both TemplatedCell (CV1) and TemplatedCell2 (CV2) during LayoutSubviews.

Before arranging, per-cell safe area insets are computed based on the cell's geometric position relative to the window's unsafe regions and passed via a new CellSafeAreaOverride property. The cell frame remains full-width so backgrounds extend edge-to-edge, while content is inset internally.

Uniform configurations (Container×4, None×4, All×4) are skipped as they are handled by the parent layout. Insets are applied only for edges explicitly set to Container or All, and ignored for SoftInput-only edges. A tolerance filter (ToSafeAreaInsets) avoids sub-pixel layout noise.

MauiView applies this override during CrossPlatformMeasure and CrossPlatformArrange when _appliesSafeAreaAdjustments is false, treating it as internal padding consistent with existing AdjustForSafeArea behavior. Stale overrides are cleared during cell reuse when the template no longer implements ISafeAreaView2.

Additionally, ContentInsetsReference is set to UIContentInsetsReference.None at all three section creation sites in LayoutFactory2 (Linear, Grid, Carousel). On iOS 26.1+, the default (.automatic.safeArea) started actively insetting cells at the section level in UICollectionViewCompositionalLayout, conflicting with MAUI's internal safe area handling. Setting .None restores pre-26.1 behavior. CV1 is unaffected as it uses UICollectionViewFlowLayout, which does not have this property.

Issues Fixed

Fixes #33604
Fixes #34635

Additional Fixes

Also resolves the following Failed iOS test cases:
VerifyGroupFooterTemplate_WithFooterString
VerifyGroupHeaderTemplate_WithFooterString
VerifyIsGroupedFalse_WithHeaderAndFooterString
SelectedItemVisualIsCleared
CollectionViewItemsShouldRespectSafeAreaEdges

Tested the behaviour in the following platforms

  • Android
  • Windows
  • iOS
  • Mac

Screenshots

Issue33604

Before Fix After Fix
iOS-withoutfix.1.mov
iOS-withfix.1.mov

Issue34635

Before Fix After Fix
BeforeFix.mov
AfterFix.mov

@dotnet-policy-service dotnet-policy-service bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Mar 26, 2026
@vishnumenon2684 vishnumenon2684 added the community ✨ Community Contribution label Mar 26, 2026
@sheiksyedm sheiksyedm marked this pull request as ready for review March 26, 2026 14:51
@sheiksyedm sheiksyedm added platform/ios area-controls-collectionview CollectionView, CarouselView, IndicatorView labels Mar 26, 2026
@sheiksyedm
Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@sheiksyedm
Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 27, 2026

🚦 Gate — Test Verification

📊 Expand Full Gatec054996 · [iOS][Android] Label: Fix RTL padding not mirroring (#32333)

Gate Result: ❌ FAILED

Platform: ios


@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 27, 2026

🤖 AI Summary

📊 Expand Full Reviewc054996 · [iOS][Android] Label: Fix RTL padding not mirroring (#32333)
🔍 Pre-Flight — Context & Validation

Issue: #33604 - CollectionView does not respect content SafeAreaEdges choices (Regression for Android/iOS)
Issue: #34635 - [MacOS] Misaligned items before resizing the window on MacOS (p/0 regression)
PR: #34667 - [iOS/MacCatalyst] Fix CollectionView not respecting SafeAreaEdges on cell content
Platforms Affected: iOS, MacCatalyst
Files Changed: 5 implementation, 1 device test
Gate Result: ❌ FAILED — CollectionViewItemsShouldRespectSafeAreaEdges (new snapshot test, no committed reference)

Key Findings

  • Root cause of [MacOS] Misaligned items before resizing the window on MacOS #34635 and CollectionView does not respect content SafeAreaEdges choices (Regression for Android, different problem in iOS) #33604 on iOS: PR [Android/iOS] Fix CollectionView not respecting SafeAreaEdges settings #33908 added _collectionViewDescendant exception in MauiView.RespondsToSafeArea() to use Window.SafeAreaInsets as base for CollectionView cells. On macOS, Window.SafeAreaInsets.Top is 41px (title bar). Combined with a timing race where IsParentHandlingSafeArea() caches false before the MAUI parent hierarchy is fully validated, every CollectionView cell receives _appliesSafeAreaAdjustments = true with a 41px top inset, causing item misalignment.
  • PR's approach: Remove _collectionViewDescendant caching. Add new CellSafeAreaOverride internal property + static ApplyCellSafeAreaOverride() + ComputeCellSafeAreaInsets() methods on MauiView. Called from TemplatedCell.LayoutSubviews() and TemplatedCell2.LayoutSubviews() before virtualView.Arrange(). CrossPlatformArrange/CrossPlatformMeasure apply override when _appliesSafeAreaAdjustments == false. LayoutFactory2 sets ContentInsetsReference = UIContentInsetsReference.None on all 3 section types (iOS 26.1+ fix).
  • Gate failure reason: CollectionViewItemsShouldRespectSafeAreaEdges is a NEW snapshot test. On the broken base branch (no fix), the test runs, creates a reference screenshot, and "passes". Gate interprets test passing-on-broken-state as the test NOT catching the bug → Gate ❌ FAILED.
  • Device test added: CollectionViewCellContentShouldBeScrollViewDescendant validates AppliesSafeAreaAdjustments == false for all MauiViews inside UICollectionView cells.
  • Separate improvement in PR: CollectionViewHandler2.iOS.cs refactors SubscribeToItemsLayoutPropertyChanged to a proper UpdateItemsLayoutSubscription/OnItemsLayoutPropertyChanged pattern with disconnect cleanup (fixes event handler leak).
  • Issues are p/0 regressions in .NET 10 SR6 milestone. [MacOS] Misaligned items before resizing the window on MacOS #34635 labeled csi-new, partner/syncfusion.
  • Prior agent review exists for commit 9fbd017 (current HEAD) with complete Try-Fix data (6 attempts).

Potential Issues

  • ComputeCellSafeAreaInsets() still uses Window.SafeAreaInsets at LayoutSubviews time — on macCatalyst, this includes the 41px title bar. For mixed-edge configurations (Container×Left + None×Top), top inset could still be non-zero for cells near the top.
  • New device test only checks AppliesSafeAreaAdjustments == false — does not validate correct insets are applied for mixed-edge SafeAreaEdges scenarios.
  • ContentInsetsReference = UIContentInsetsReference.None needed for iOS 26.1+ but not tested independently from the main fix.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #34667 Remove _collectionViewDescendant; inject CellSafeAreaOverride from TemplatedCell before Arrange; ContentInsetsReference=None in LayoutFactory2 ❌ FAILED (Gate) MauiView.cs, TemplatedCell.cs, TemplatedCell2.cs, LayoutFactory2.cs Gate failed: new snapshot test has no reference

🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix (claude-opus-4.6) UICollectionViewCell Superview short-circuit by walking UIKit Superview chain for UICollectionViewCell ancestor Detection PASS (40 passed, 0 failed, 4 ignored) MauiView.cs (+28 lines), LayoutFactory2.cs (+5 lines) Uses UIKit hierarchy; no parallel code paths
2 try-fix (claude-sonnet-4.6) Expand add ancestry check alongside check in detection PASS (80 passed, 0 failed) MauiView.cs (+5/-1 lines) Minimal single-file; fixes timing at earliest decision point
3 try-fix (gpt-5.3-codex) Override to invalidate + PASS (40 passed, 0 failed, 4 ignored) MauiView.cs (+9 lines) Lifecycle-based; no detection logic change
4 try-fix (gpt-5.4) Call from TemplatedCell/TemplatedCell2 before Arrange PASS (160 passed, 0 failed) TemplatedCell.cs, TemplatedCell2.cs Cell-side fix at arrange call site
5 try-fix (claude-sonnet-4.6, round 2) Reset in alongside existing PASS (200 passed, 0 failed) MauiView.cs (+1 line) Minimal: clears stale scroll-view-descendant cache when safe area fires; mirrors existing invalidation pattern
PR PR #34667 Remove _collectionViewDescendant; inject CellSafeAreaOverride from TemplatedCell before Arrange; ContentInsetsReference=None in FAILED (Gate) MauiView.cs, TemplatedCell.cs, TemplatedCell2.cs, LayoutFactory2.cs Gate failed: new snapshot test has no committed reference LayoutFactory2

Cross-Pollination

Model Round New Ideas? Details
led to Attempt 5
became Attempt 5
gpt-5.3-codex 2 YES Defer/don't cache when Window == null; async recompute
claude-opus-4.6 3 NO NEW IDEAS Attempt 5 is optimal: +1 line is theoretical minimum for a caching bug
claude-sonnet-4.6 3 Marginal Check scroll view TYPE (not UICollectionView/ more complex than Attempt 5
gpt-5.3-codex 3 Marginal Also add _scrollViewDescendant = null to InvalidateSafeArea() (+2 lines broader but not needed given tests pass

Exhausted: Yes (3 rounds reached; no new ideas that beat Attempt 5)
Selected Fix: Attempt _scrollViewDescendant = null in SafeAreaInsetsDidChange() (+1 line, MauiView.cs only)5

Reason: Attempt 5 is the most minimal correct fix: one line in MauiView.cs that mirrors the existing _parentHandlesSafeArea = null reset pattern in the same method. It targets the root cause precisely (stale _scrollViewDescendant cache never cleared when SafeAreaInsetsDidChange fires) with zero new code paths, zero new properties, and zero changes to TemplatedCell or LayoutFactory2. All 200 CollectionView device tests pass.


📋 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issues #33604, #34635; iOS/MacCatalyst; 5 impl files + 1 device test
Gate ❌ FAILED CollectionViewItemsShouldRespectSafeAreaEdges — new snapshot test has no committed reference; passes on broken branch
Try-Fix ✅ COMPLETE 5 attempts (all PASS), cross-pollination exhausted (3 rounds)
Report ✅ COMPLETE

Summary

PR #34667 fixes a p/0 regression (#34635) and related issue #33604 where CollectionView cells on iOS/MacCatalyst incorrectly apply safe area insets due to a timing race in MauiView._parentHandlesSafeArea/_scrollViewDescendant caching. The gate failed because the new snapshot test CollectionViewItemsShouldRespectSafeAreaEdges has no committed reference screenshot, causing it to pass on the broken baseline (test creates its own reference). A simpler and more correct alternative fix was found via Try-Fix.

Root Cause

PR #33908 added a _collectionViewDescendant exception that used Window.SafeAreaInsets as the safe area base for CollectionView cells. On macCatalyst, Window.SafeAreaInsets.Top is 41px (title bar). Combined with a timing race where _scrollViewDescendant is cached as false during the initial UICollectionView layout pass (before the cell is in the full hierarchy), RespondsToSafeArea() returns true and cells apply _appliesSafeAreaAdjustments = true with a 41px top inset — causing all items to be misaligned.

The fundamental issue: _scrollViewDescendant is cached but never cleared when SafeAreaInsetsDidChange() fires. The stale false value persists through subsequent layout passes.

Fix Quality

PR's fix concerns:

  • Gate ❌ FAILED: CollectionViewItemsShouldRespectSafeAreaEdges snapshot test has no committed reference — it passes on the broken baseline, making it invalid as a gate test. The snapshot reference needs to be committed before this test can validate the fix.
  • Adds a new CellSafeAreaOverride internal property, ApplyCellSafeAreaOverride() static method, and ComputeCellSafeAreaInsets() with geometry-based per-cell inset computation — a parallel safe area code path alongside _safeArea/_appliesSafeAreaAdjustments
  • ComputeCellSafeAreaInsets() still uses Window.SafeAreaInsets at LayoutSubviews time — on macCatalyst, this includes the 41px title bar; for mixed-edge configurations the issue could recur
  • New device test only checks AppliesSafeAreaAdjustments == false, not that correct insets are applied
  • Modifies 4+ files; introduces complexity in both TemplatedCell files
  • ContentInsetsReference = UIContentInsetsReference.None (iOS 26.1+ fix) is bundled — should be evaluated independently

Selected Alternative — Attempt 5 (+1 line in MauiView.cs):

 public override void SafeAreaInsetsDidChange()
 {
+    _scrollViewDescendant = null;
     _safeAreaInvalidated = true;
     _parentHandlesSafeArea = null;
     base.SafeAreaInsetsDidChange();
  • Single file, single line addition
  • Mirrors the existing _parentHandlesSafeArea = null reset — consistent with established pattern
  • No new properties, no new code paths, no changes to TemplatedCell
  • Fixes the root cause: stale _scrollViewDescendant cache is now cleared when SafeAreaInsetsDidChange fires; next RespondsToSafeArea() call walks a fully-connected hierarchy and correctly returns false
  • 200 CollectionView device tests pass

Recommendation: Replace the PR's implementation with Attempt 5. Also consider:

  1. Whether ContentInsetsReference = UIContentInsetsReference.None in LayoutFactory2 is separately needed for iOS 26.1+ — it could be added alongside Attempt 5 if confirmed necessary
  2. Fix the gate test: commit a reference screenshot or convert CollectionViewItemsShouldRespectSafeAreaEdges to a non-snapshot assertion

@MauiBot MauiBot added s/agent-review-incomplete AI agent could not complete all phases (blocker, timeout, error) s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) s/agent-changes-requested AI agent recommends changes - found a better alternative or issues and removed s/agent-review-incomplete AI agent could not complete all phases (blocker, timeout, error) labels Mar 27, 2026
@MauiBot MauiBot added s/agent-fix-win AI found a better alternative fix than the PR s/agent-review-incomplete AI agent could not complete all phases (blocker, timeout, error) s/agent-changes-requested AI agent recommends changes - found a better alternative or issues and removed s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-review-incomplete AI agent could not complete all phases (blocker, timeout, error) labels Mar 27, 2026
@kubaflo kubaflo changed the base branch from inflight/candidate to main March 28, 2026 17:54
@NirmalKumarYuvaraj NirmalKumarYuvaraj changed the base branch from main to inflight/candidate March 30, 2026 07:11
@praveenkumarkarunanithi praveenkumarkarunanithi changed the title [iOS/MacCatalyst] Fix CollectionView not respecting SafeAreaEdges on cell content [iOS/MacCatalyst] Fix CollectionView cell misalignment regression on candidate branch Mar 30, 2026
@praveenkumarkarunanithi
Copy link
Copy Markdown
Contributor Author

🤖 AI Summary

📊 Expand Full Reviewc054996 · [iOS][Android] Label: Fix RTL padding not mirroring (#32333)
🔍 Pre-Flight — Context & Validation
Issue: #33604 - CollectionView does not respect content SafeAreaEdges choices (Regression for Android/iOS) Issue: #34635 - [MacOS] Misaligned items before resizing the window on MacOS (p/0 regression) PR: #34667 - [iOS/MacCatalyst] Fix CollectionView not respecting SafeAreaEdges on cell content Platforms Affected: iOS, MacCatalyst Files Changed: 5 implementation, 1 device test Gate Result: ❌ FAILED — CollectionViewItemsShouldRespectSafeAreaEdges (new snapshot test, no committed reference)

Key Findings

  • Root cause of [MacOS] Misaligned items before resizing the window on MacOS #34635 and CollectionView does not respect content SafeAreaEdges choices (Regression for Android, different problem in iOS) #33604 on iOS: PR [Android/iOS] Fix CollectionView not respecting SafeAreaEdges settings #33908 added _collectionViewDescendant exception in MauiView.RespondsToSafeArea() to use Window.SafeAreaInsets as base for CollectionView cells. On macOS, Window.SafeAreaInsets.Top is 41px (title bar). Combined with a timing race where IsParentHandlingSafeArea() caches false before the MAUI parent hierarchy is fully validated, every CollectionView cell receives _appliesSafeAreaAdjustments = true with a 41px top inset, causing item misalignment.
  • PR's approach: Remove _collectionViewDescendant caching. Add new CellSafeAreaOverride internal property + static ApplyCellSafeAreaOverride() + ComputeCellSafeAreaInsets() methods on MauiView. Called from TemplatedCell.LayoutSubviews() and TemplatedCell2.LayoutSubviews() before virtualView.Arrange(). CrossPlatformArrange/CrossPlatformMeasure apply override when _appliesSafeAreaAdjustments == false. LayoutFactory2 sets ContentInsetsReference = UIContentInsetsReference.None on all 3 section types (iOS 26.1+ fix).
  • Gate failure reason: CollectionViewItemsShouldRespectSafeAreaEdges is a NEW snapshot test. On the broken base branch (no fix), the test runs, creates a reference screenshot, and "passes". Gate interprets test passing-on-broken-state as the test NOT catching the bug → Gate ❌ FAILED.
  • Device test added: CollectionViewCellContentShouldBeScrollViewDescendant validates AppliesSafeAreaAdjustments == false for all MauiViews inside UICollectionView cells.
  • Separate improvement in PR: CollectionViewHandler2.iOS.cs refactors SubscribeToItemsLayoutPropertyChanged to a proper UpdateItemsLayoutSubscription/OnItemsLayoutPropertyChanged pattern with disconnect cleanup (fixes event handler leak).
  • Issues are p/0 regressions in .NET 10 SR6 milestone. [MacOS] Misaligned items before resizing the window on MacOS #34635 labeled csi-new, partner/syncfusion.
  • Prior agent review exists for commit 9fbd017 (current HEAD) with complete Try-Fix data (6 attempts).

Potential Issues

  • ComputeCellSafeAreaInsets() still uses Window.SafeAreaInsets at LayoutSubviews time — on macCatalyst, this includes the 41px title bar. For mixed-edge configurations (Container×Left + None×Top), top inset could still be non-zero for cells near the top.
  • New device test only checks AppliesSafeAreaAdjustments == false — does not validate correct insets are applied for mixed-edge SafeAreaEdges scenarios.
  • ContentInsetsReference = UIContentInsetsReference.None needed for iOS 26.1+ but not tested independently from the main fix.

Fix Candidates

Source Approach Test Result Files Changed Notes

PR PR #34667 Remove _collectionViewDescendant; inject CellSafeAreaOverride from TemplatedCell before Arrange; ContentInsetsReference=None in LayoutFactory2 ❌ FAILED (Gate) MauiView.cs, TemplatedCell.cs, TemplatedCell2.cs, LayoutFactory2.cs Gate failed: new snapshot test has no reference
🔧 Fix — Analysis & Comparison

Fix Candidates

Source Approach Test Result Files Changed Notes

1 try-fix (claude-opus-4.6) UICollectionViewCell Superview short-circuit by walking UIKit Superview chain for UICollectionViewCell ancestor Detection PASS (40 passed, 0 failed, 4 ignored) MauiView.cs (+28 lines), LayoutFactory2.cs (+5 lines) Uses UIKit hierarchy; no parallel code paths
2 try-fix (claude-sonnet-4.6) Expand add ancestry check alongside check in detection PASS (80 passed, 0 failed) MauiView.cs (+5/-1 lines) Minimal single-file; fixes timing at earliest decision point
3 try-fix (gpt-5.3-codex) Override to invalidate + PASS (40 passed, 0 failed, 4 ignored) MauiView.cs (+9 lines) Lifecycle-based; no detection logic change
4 try-fix (gpt-5.4) Call from TemplatedCell/TemplatedCell2 before Arrange PASS (160 passed, 0 failed) TemplatedCell.cs, TemplatedCell2.cs Cell-side fix at arrange call site
5 try-fix (claude-sonnet-4.6, round 2) Reset in alongside existing PASS (200 passed, 0 failed) MauiView.cs (+1 line) Minimal: clears stale scroll-view-descendant cache when safe area fires; mirrors existing invalidation pattern
PR PR #34667 Remove _collectionViewDescendant; inject CellSafeAreaOverride from TemplatedCell before Arrange; ContentInsetsReference=None in FAILED (Gate) MauiView.cs, TemplatedCell.cs, TemplatedCell2.cs, LayoutFactory2.cs Gate failed: new snapshot test has no committed reference LayoutFactory2

Cross-Pollination

Model Round New Ideas? Details
led to Attempt 5
became Attempt 5
gpt-5.3-codex 2 YES Defer/don't cache when Window == null; async recompute
claude-opus-4.6 3 NO NEW IDEAS Attempt 5 is optimal: +1 line is theoretical minimum for a caching bug
claude-sonnet-4.6 3 Marginal Check scroll view TYPE (not UICollectionView/ more complex than Attempt 5
gpt-5.3-codex 3 Marginal Also add _scrollViewDescendant = null to InvalidateSafeArea() (+2 lines broader but not needed given tests pass
Exhausted: Yes (3 rounds reached; no new ideas that beat Attempt 5) Selected Fix: Attempt _scrollViewDescendant = null in SafeAreaInsetsDidChange() (+1 line, MauiView.cs only)5

Reason: Attempt 5 is the most minimal correct fix: one line in MauiView.cs that mirrors the existing _parentHandlesSafeArea = null reset pattern in the same method. It targets the root cause precisely (stale _scrollViewDescendant cache never cleared when SafeAreaInsetsDidChange fires) with zero new code paths, zero new properties, and zero changes to TemplatedCell or LayoutFactory2. All 200 CollectionView device tests pass.

📋 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issues #33604, #34635; iOS/MacCatalyst; 5 impl files + 1 device test
Gate ❌ FAILED CollectionViewItemsShouldRespectSafeAreaEdges — new snapshot test has no committed reference; passes on broken branch
Try-Fix ✅ COMPLETE 5 attempts (all PASS), cross-pollination exhausted (3 rounds)
Report ✅ COMPLETE

Summary

PR #34667 fixes a p/0 regression (#34635) and related issue #33604 where CollectionView cells on iOS/MacCatalyst incorrectly apply safe area insets due to a timing race in MauiView._parentHandlesSafeArea/_scrollViewDescendant caching. The gate failed because the new snapshot test CollectionViewItemsShouldRespectSafeAreaEdges has no committed reference screenshot, causing it to pass on the broken baseline (test creates its own reference). A simpler and more correct alternative fix was found via Try-Fix.

Root Cause

PR #33908 added a _collectionViewDescendant exception that used Window.SafeAreaInsets as the safe area base for CollectionView cells. On macCatalyst, Window.SafeAreaInsets.Top is 41px (title bar). Combined with a timing race where _scrollViewDescendant is cached as false during the initial UICollectionView layout pass (before the cell is in the full hierarchy), RespondsToSafeArea() returns true and cells apply _appliesSafeAreaAdjustments = true with a 41px top inset — causing all items to be misaligned.

The fundamental issue: _scrollViewDescendant is cached but never cleared when SafeAreaInsetsDidChange() fires. The stale false value persists through subsequent layout passes.

Fix Quality

PR's fix concerns:

  • Gate ❌ FAILED: CollectionViewItemsShouldRespectSafeAreaEdges snapshot test has no committed reference — it passes on the broken baseline, making it invalid as a gate test. The snapshot reference needs to be committed before this test can validate the fix.
  • Adds a new CellSafeAreaOverride internal property, ApplyCellSafeAreaOverride() static method, and ComputeCellSafeAreaInsets() with geometry-based per-cell inset computation — a parallel safe area code path alongside _safeArea/_appliesSafeAreaAdjustments
  • ComputeCellSafeAreaInsets() still uses Window.SafeAreaInsets at LayoutSubviews time — on macCatalyst, this includes the 41px title bar; for mixed-edge configurations the issue could recur
  • New device test only checks AppliesSafeAreaAdjustments == false, not that correct insets are applied
  • Modifies 4+ files; introduces complexity in both TemplatedCell files
  • ContentInsetsReference = UIContentInsetsReference.None (iOS 26.1+ fix) is bundled — should be evaluated independently

Selected Alternative — Attempt 5 (+1 line in MauiView.cs):

 public override void SafeAreaInsetsDidChange()
 {
+    _scrollViewDescendant = null;
     _safeAreaInvalidated = true;
     _parentHandlesSafeArea = null;
     base.SafeAreaInsetsDidChange();
  • Single file, single line addition
  • Mirrors the existing _parentHandlesSafeArea = null reset — consistent with established pattern
  • No new properties, no new code paths, no changes to TemplatedCell
  • Fixes the root cause: stale _scrollViewDescendant cache is now cleared when SafeAreaInsetsDidChange fires; next RespondsToSafeArea() call walks a fully-connected hierarchy and correctly returns false
  • 200 CollectionView device tests pass

Recommendation: Replace the PR's implementation with Attempt 5. Also consider:

  1. Whether ContentInsetsReference = UIContentInsetsReference.None in LayoutFactory2 is separately needed for iOS 26.1+ — it could be added alongside Attempt 5 if confirmed necessary
  2. Fix the gate test: commit a reference screenshot or convert CollectionViewItemsShouldRespectSafeAreaEdges to a non-snapshot assertion

Attempt 5 fails to resolve #34635 as the root cause is a fundamental coordinate mismatch, not a stale cache; Window.SafeAreaInsets incorrectly reports a constant 41px on macCatalyst.

Current fix resolves this by geometrically computing per-cell insets in TemplatedCell.LayoutSubviews, with the ContentInsetsReference override now strictly conditioned for iOS 26+.

@sheiksyedm
Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

@sheiksyedm
Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@sheiksyedm sheiksyedm requested a review from kubaflo March 31, 2026 09:50
@kubaflo kubaflo merged commit 6459bbd into inflight/candidate Mar 31, 2026
12 of 41 checks passed
@kubaflo kubaflo deleted the fix-34635 branch March 31, 2026 10:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-controls-collectionview CollectionView, CarouselView, IndicatorView community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/ios s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

5 participants