@@ -63,13 +63,77 @@ public abstract class MauiView : UIView, ICrossPlatformLayoutBacking, IVisualTre
6363 bool _appliesSafeAreaAdjustments ;
6464
6565 /// <summary>
66- /// Safe area override injected by TemplatedCell2 for CollectionView cells.
66+ /// Safe area override injected by CollectionView cells.
6767 /// UICollectionView bypasses MAUI's arrange chain, so cells cannot use <see cref="_safeArea"/>.
6868 /// Applied as internal padding by <see cref="CrossPlatformArrange"/> and
6969 /// <see cref="CrossPlatformMeasure"/> (#33604, #34635).
7070 /// </summary>
7171 internal SafeAreaPadding CellSafeAreaOverride { get ; set ; } = SafeAreaPadding . Empty ;
7272
73+ /// <summary>
74+ /// Computes and applies per-cell safe area insets for a CollectionView cell.
75+ /// Called from TemplatedCell/TemplatedCell2 LayoutSubviews before Arrange.
76+ /// </summary>
77+ internal static void ApplyCellSafeAreaOverride ( UIView cell , IView virtualView , UIView platformView )
78+ {
79+ if ( virtualView is ISafeAreaView2 safeView && platformView is MauiView mauiView )
80+ {
81+ var insets = ComputeCellSafeAreaInsets ( cell , safeView ) ;
82+ mauiView . CellSafeAreaOverride = insets != UIEdgeInsets . Zero
83+ ? insets . ToSafeAreaInsets ( )
84+ : SafeAreaPadding . Empty ;
85+ }
86+ else if ( platformView is MauiView mv && ! mv . CellSafeAreaOverride . IsEmpty )
87+ {
88+ // Clear stale override from a previous template that implemented ISafeAreaView2.
89+ mv . CellSafeAreaOverride = SafeAreaPadding . Empty ;
90+ }
91+ }
92+
93+ /// <summary>
94+ /// Computes per-cell safe area insets based on geometric overlap with the window's unsafe regions.
95+ /// Returns <see cref="UIEdgeInsets.Zero"/> when all edges share the same region (e.g., default
96+ /// Container×4), as the parent layout chain handles uniform safe area (#33604, #34635).
97+ /// </summary>
98+ static UIEdgeInsets ComputeCellSafeAreaInsets ( UIView cell , ISafeAreaView2 safeView )
99+ {
100+ var window = cell . Window ;
101+ if ( window is null )
102+ return UIEdgeInsets . Zero ;
103+
104+ var windowSA = window . SafeAreaInsets ;
105+ if ( windowSA == UIEdgeInsets . Zero )
106+ return UIEdgeInsets . Zero ;
107+
108+ var leftRegion = safeView . GetSafeAreaRegionsForEdge ( 0 ) ;
109+ var topRegion = safeView . GetSafeAreaRegionsForEdge ( 1 ) ;
110+ var rightRegion = safeView . GetSafeAreaRegionsForEdge ( 2 ) ;
111+ var bottomRegion = safeView . GetSafeAreaRegionsForEdge ( 3 ) ;
112+
113+ // Uniform edges (Container×4, None×4, All×4) are handled by the parent layout chain.
114+ bool allSameRegion = leftRegion == topRegion
115+ && topRegion == rightRegion
116+ && rightRegion == bottomRegion ;
117+
118+ if ( allSameRegion )
119+ return UIEdgeInsets . Zero ;
120+
121+ // Only apply insets for Container edges; SoftInput-only edges are excluded.
122+ var cellInWindow = cell . ConvertRectToView ( cell . Bounds , window ) ;
123+ var windowBounds = window . Bounds ;
124+
125+ nfloat left = SafeAreaEdges . IsContainer ( leftRegion ) && windowSA . Left > 0
126+ ? ( nfloat ) Math . Max ( 0 , ( double ) ( windowSA . Left - cellInWindow . X ) ) : 0 ;
127+ nfloat top = SafeAreaEdges . IsContainer ( topRegion ) && windowSA . Top > 0
128+ ? ( nfloat ) Math . Max ( 0 , ( double ) ( windowSA . Top - cellInWindow . Y ) ) : 0 ;
129+ nfloat right = SafeAreaEdges . IsContainer ( rightRegion ) && windowSA . Right > 0
130+ ? ( nfloat ) Math . Max ( 0 , ( double ) ( cellInWindow . Right - ( windowBounds . Width - windowSA . Right ) ) ) : 0 ;
131+ nfloat bottom = SafeAreaEdges . IsContainer ( bottomRegion ) && windowSA . Bottom > 0
132+ ? ( nfloat ) Math . Max ( 0 , ( double ) ( cellInWindow . Bottom - ( windowBounds . Height - windowSA . Bottom ) ) ) : 0 ;
133+
134+ return new UIEdgeInsets ( top , left , bottom , right ) ;
135+ }
136+
73137 // Indicates whether this view should respond to safe area insets.
74138 // Cached to avoid repeated hierarchy checks.
75139 // True if the view is an ISafeAreaView, does not ignore safe area, and is not inside a UIScrollView;
0 commit comments