|
6 | 6 | using Microsoft.Maui.Controls.Internals; |
7 | 7 | using Microsoft.Maui.Controls.Platform; |
8 | 8 | using Microsoft.Maui.Graphics; |
| 9 | +using Microsoft.Maui.Platform; |
9 | 10 | using UIKit; |
10 | 11 |
|
11 | 12 | namespace Microsoft.Maui.Controls.Handlers.Items2 |
@@ -197,10 +198,71 @@ public override void LayoutSubviews() |
197 | 198 | // We now have to apply the new bounds size to the virtual view |
198 | 199 | // which will automatically set the frame on the platform view too. |
199 | 200 | var frame = new Rect(Point.Zero, boundsSize); |
| 201 | + |
| 202 | + // Inject per-cell safe area insets into the MauiView for CrossPlatformArrange |
| 203 | + // to apply as internal padding. UICollectionView bypasses MAUI's arrange chain, |
| 204 | + // so cells cannot use the standard safe area flow (#33604, #34635). |
| 205 | + if (virtualView is ISafeAreaView2 safeView && PlatformView is MauiView mauiView) |
| 206 | + { |
| 207 | + var insets = ComputeCellSafeAreaInsets(safeView); |
| 208 | + mauiView.CellSafeAreaOverride = insets != UIEdgeInsets.Zero |
| 209 | + ? insets.ToSafeAreaInsets() |
| 210 | + : SafeAreaPadding.Empty; |
| 211 | + } |
| 212 | + else if (PlatformView is MauiView mv && !mv.CellSafeAreaOverride.IsEmpty) |
| 213 | + { |
| 214 | + // Clear stale override from a previous template that implemented ISafeAreaView2. |
| 215 | + mv.CellSafeAreaOverride = SafeAreaPadding.Empty; |
| 216 | + } |
| 217 | + |
200 | 218 | virtualView.Arrange(frame); |
201 | 219 | } |
202 | 220 | } |
203 | 221 |
|
| 222 | + /// <summary> |
| 223 | + /// Computes per-cell safe area insets based on geometric overlap with the window's unsafe regions. |
| 224 | + /// Returns <see cref="UIEdgeInsets.Zero"/> when all edges share the same region (e.g., default |
| 225 | + /// Container×4), as the parent layout chain handles uniform safe area (#33604, #34635). |
| 226 | + /// </summary> |
| 227 | + UIEdgeInsets ComputeCellSafeAreaInsets(ISafeAreaView2 safeView) |
| 228 | + { |
| 229 | + var window = Window; |
| 230 | + if (window is null) |
| 231 | + return UIEdgeInsets.Zero; |
| 232 | + |
| 233 | + var windowSA = window.SafeAreaInsets; |
| 234 | + if (windowSA == UIEdgeInsets.Zero) |
| 235 | + return UIEdgeInsets.Zero; |
| 236 | + |
| 237 | + var leftRegion = safeView.GetSafeAreaRegionsForEdge(0); |
| 238 | + var topRegion = safeView.GetSafeAreaRegionsForEdge(1); |
| 239 | + var rightRegion = safeView.GetSafeAreaRegionsForEdge(2); |
| 240 | + var bottomRegion = safeView.GetSafeAreaRegionsForEdge(3); |
| 241 | + |
| 242 | + // Uniform edges (Container×4, None×4, All×4) are handled by the parent layout chain. |
| 243 | + bool allSameRegion = leftRegion == topRegion |
| 244 | + && topRegion == rightRegion |
| 245 | + && rightRegion == bottomRegion; |
| 246 | + |
| 247 | + if (allSameRegion) |
| 248 | + return UIEdgeInsets.Zero; |
| 249 | + |
| 250 | + // Only apply insets for Container edges; SoftInput-only edges are excluded. |
| 251 | + var cellInWindow = ConvertRectToView(Bounds, window); |
| 252 | + var windowBounds = window.Bounds; |
| 253 | + |
| 254 | + nfloat left = SafeAreaEdges.IsContainer(leftRegion) && windowSA.Left > 0 |
| 255 | + ? (nfloat)Math.Max(0, (double)(windowSA.Left - cellInWindow.X)) : 0; |
| 256 | + nfloat top = SafeAreaEdges.IsContainer(topRegion) && windowSA.Top > 0 |
| 257 | + ? (nfloat)Math.Max(0, (double)(windowSA.Top - cellInWindow.Y)) : 0; |
| 258 | + nfloat right = SafeAreaEdges.IsContainer(rightRegion) && windowSA.Right > 0 |
| 259 | + ? (nfloat)Math.Max(0, (double)(cellInWindow.Right - (windowBounds.Width - windowSA.Right))) : 0; |
| 260 | + nfloat bottom = SafeAreaEdges.IsContainer(bottomRegion) && windowSA.Bottom > 0 |
| 261 | + ? (nfloat)Math.Max(0, (double)(cellInWindow.Bottom - (windowBounds.Height - windowSA.Bottom))) : 0; |
| 262 | + |
| 263 | + return new UIEdgeInsets(top, left, bottom, right); |
| 264 | + } |
| 265 | + |
204 | 266 | public override void PrepareForReuse() |
205 | 267 | { |
206 | 268 | //Unbind(); |
|
0 commit comments