Skip to content

Commit 8a1c06b

Browse files
praveenkumarkarunanithisheiksyedm
authored andcommitted
Revert accidental fix commits from inflight/candidate
Reverts 7ddc0a1, 49e9512, 117fd00, ca29f7a, 492769c which were accidentally cherry-picked onto inflight/candidate. These fix commits belong on the fix-34635 feature branch only.
1 parent 29911a8 commit 8a1c06b

File tree

4 files changed

+42
-164
lines changed

4 files changed

+42
-164
lines changed

src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,6 @@ public override void LayoutSubviews()
136136
// We now have to apply the new bounds size to the virtual view
137137
// which will automatically set the frame on the platform view too.
138138
var frame = new Rect(Point.Zero, boundsSize);
139-
140-
// Inject per-cell safe area insets into the MauiView for CrossPlatformArrange
141-
// to apply as internal padding. UICollectionView bypasses MAUI's arrange chain,
142-
// so cells cannot use the standard safe area flow (#33604, #34635).
143-
MauiView.ApplyCellSafeAreaOverride(this, virtualView, PlatformHandler.ToPlatform());
144-
145139
virtualView.Arrange(frame);
146140
}
147141
}

src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,6 @@ public override void LayoutSubviews()
197197
// We now have to apply the new bounds size to the virtual view
198198
// which will automatically set the frame on the platform view too.
199199
var frame = new Rect(Point.Zero, boundsSize);
200-
201-
// Inject per-cell safe area insets into the MauiView for CrossPlatformArrange
202-
// to apply as internal padding. UICollectionView bypasses MAUI's arrange chain,
203-
// so cells cannot use the standard safe area flow (#33604, #34635).
204-
MauiView.ApplyCellSafeAreaOverride(this, virtualView, PlatformView);
205-
206200
virtualView.Arrange(frame);
207201
}
208202
}

src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -319,70 +319,5 @@ Rect GetCollectionViewCellBounds(IView cellContent)
319319

320320
return cellContent.ToPlatform().GetParentOfType<UIKit.UICollectionViewCell>().GetBoundingBox();
321321
}
322-
323-
// Regression test for https://github.com/dotnet/maui/issues/34635
324-
[Fact("CollectionView cell MauiViews should be treated as UIScrollView descendants and not apply safe area independently")]
325-
[Category(TestCategory.CollectionView)]
326-
public async Task CollectionViewCellContentShouldBeScrollViewDescendant()
327-
{
328-
SetupBuilder();
329-
330-
var collectionView = new CollectionView
331-
{
332-
ItemsSource = Enumerable.Range(0, 5).Select(i => $"Item {i}").ToList(),
333-
ItemTemplate = new DataTemplate(() =>
334-
{
335-
var grid = new Grid { Padding = new Thickness(10) };
336-
var label = new Label();
337-
label.SetBinding(Label.TextProperty, ".");
338-
grid.Add(label);
339-
return grid;
340-
}),
341-
};
342-
343-
await CreateHandlerAndAddToWindow<CollectionViewHandler>(collectionView, async handler =>
344-
{
345-
await Task.Delay(500);
346-
347-
var platformCV = collectionView.ToPlatform();
348-
Assert.NotNull(platformCV);
349-
350-
var uiCollectionView = platformCV as UICollectionView
351-
?? platformCV.GetParentOfType<UICollectionView>();
352-
353-
if (uiCollectionView is null && platformCV is UIView pv)
354-
uiCollectionView = pv.Subviews.OfType<UICollectionView>().FirstOrDefault();
355-
356-
Assert.NotNull(uiCollectionView);
357-
358-
var visibleCells = uiCollectionView.VisibleCells;
359-
Assert.NotEmpty(visibleCells);
360-
361-
foreach (var cell in visibleCells)
362-
{
363-
foreach (var mv in FindAllSubviews<MauiView>(cell))
364-
{
365-
mv.SetNeedsLayout();
366-
mv.LayoutIfNeeded();
367-
368-
Assert.False(mv.AppliesSafeAreaAdjustments,
369-
$"CollectionView cell MauiView '{mv.View?.GetType().Name}' should not apply safe area adjustments. " +
370-
"Cell views inside UICollectionView must be treated as scroll view descendants.");
371-
}
372-
}
373-
});
374-
}
375-
376-
static List<T> FindAllSubviews<T>(UIView root) where T : UIView
377-
{
378-
var result = new List<T>();
379-
foreach (var subview in root.Subviews)
380-
{
381-
if (subview is T match)
382-
result.Add(match);
383-
result.AddRange(FindAllSubviews<T>(subview));
384-
}
385-
return result;
386-
}
387322
}
388323
}

src/Core/src/Platform/iOS/MauiView.cs

Lines changed: 42 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -62,78 +62,6 @@ public abstract class MauiView : UIView, ICrossPlatformLayoutBacking, IVisualTre
6262
/// </summary>
6363
bool _appliesSafeAreaAdjustments;
6464

65-
/// <summary>
66-
/// Safe area override injected by CollectionView cells.
67-
/// UICollectionView bypasses MAUI's arrange chain, so cells cannot use <see cref="_safeArea"/>.
68-
/// Applied as internal padding by <see cref="CrossPlatformArrange"/> and
69-
/// <see cref="CrossPlatformMeasure"/> (#33604, #34635).
70-
/// </summary>
71-
internal SafeAreaPadding CellSafeAreaOverride { get; set; } = SafeAreaPadding.Empty;
72-
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-
13765
// Indicates whether this view should respond to safe area insets.
13866
// Cached to avoid repeated hierarchy checks.
13967
// True if the view is an ISafeAreaView, does not ignore safe area, and is not inside a UIScrollView;
@@ -144,6 +72,13 @@ static UIEdgeInsets ComputeCellSafeAreaInsets(UIView cell, ISafeAreaView2 safeVi
14472
// Null means not yet determined. Invalidated when view hierarchy changes.
14573
bool? _parentHandlesSafeArea;
14674

75+
// Cached UICollectionView parent detection to avoid repeated hierarchy checks.
76+
bool? _collectionViewDescendant;
77+
78+
// Cached Window safe area padding for CollectionView children to detect changes.
79+
// Uses SafeAreaPadding with EqualsAtPixelLevel() to absorb sub-pixel animation noise.
80+
SafeAreaPadding _lastWindowSafeAreaPadding;
81+
14782
// Keyboard tracking
14883
CGRect _keyboardFrame = CGRect.Empty;
14984
bool _isKeyboardShowing;
@@ -197,9 +132,18 @@ bool RespondsToSafeArea()
197132
// To prevent this, we ignore safe area calculations on child views when they are inside a scroll view.
198133
// The scrollview itself is responsible for applying the correct insets, and child views should not apply additional safe area logic.
199134
//
135+
// EXCEPTION: CollectionView items must handle their own safe area because UICollectionView (which inherits from UIScrollView)
136+
// does not automatically apply safe area insets to individual cells. Without this exception, CollectionView content
137+
// would render under the notch and home indicator.
138+
//
200139
// For more details and implementation specifics, see MauiScrollView.cs, which contains the logic for safe area management
201140
// within scroll views and explains how this interacts with the overall layout system.
202-
_scrollViewDescendant = this.GetParentOfType<UIScrollView>() is not null;
141+
var scrollViewParent = this.GetParentOfType<UIScrollView>();
142+
_scrollViewDescendant = scrollViewParent is not null && scrollViewParent is not UICollectionView;
143+
144+
// Cache whether this view is inside a UICollectionView for use in CrossPlatformArrange()
145+
_collectionViewDescendant = scrollViewParent is UICollectionView;
146+
203147
return !_scrollViewDescendant.Value;
204148
}
205149

@@ -366,7 +310,12 @@ void OnKeyboardWillHide(NSNotification notification)
366310

367311
SafeAreaPadding GetAdjustedSafeAreaInsets()
368312
{
369-
var baseSafeArea = SafeAreaInsets.ToSafeAreaInsets();
313+
// CollectionView cells don't receive SafeAreaInsetsDidChange notifications, so their SafeAreaInsets
314+
// property may be stale during layout (especially after rotation). Use Window.SafeAreaInsets instead,
315+
// which always reflects the current device orientation and safe area state.
316+
var baseSafeArea = _collectionViewDescendant == true && Window is not null
317+
? Window.SafeAreaInsets.ToSafeAreaInsets()
318+
: SafeAreaInsets.ToSafeAreaInsets();
370319

371320
// Check if keyboard-aware safe area adjustments are needed
372321
if (View is ISafeAreaView2 safeAreaPage && _isKeyboardShowing)
@@ -571,23 +520,19 @@ public ICrossPlatformLayout? CrossPlatformLayout
571520
/// <returns>The desired size of the view</returns>
572521
Size CrossPlatformMeasure(double widthConstraint, double heightConstraint)
573522
{
574-
var effectiveSafeArea = _appliesSafeAreaAdjustments ? _safeArea
575-
: !CellSafeAreaOverride.IsEmpty ? CellSafeAreaOverride
576-
: SafeAreaPadding.Empty;
577-
578-
if (!effectiveSafeArea.IsEmpty)
523+
if (_appliesSafeAreaAdjustments)
579524
{
580525
// When responding to safe area, we need to adjust the constraints to account for the safe area.
581-
widthConstraint -= effectiveSafeArea.HorizontalThickness;
582-
heightConstraint -= effectiveSafeArea.VerticalThickness;
526+
widthConstraint -= _safeArea.HorizontalThickness;
527+
heightConstraint -= _safeArea.VerticalThickness;
583528
}
584529

585530
var crossPlatformSize = CrossPlatformLayout?.CrossPlatformMeasure(widthConstraint, heightConstraint) ?? Size.Zero;
586531

587-
if (!effectiveSafeArea.IsEmpty)
532+
if (_appliesSafeAreaAdjustments)
588533
{
589534
// If we're responding to the safe area, we need to add the safe area back to the size so the container can allocate the correct space
590-
crossPlatformSize = new Size(crossPlatformSize.Width + effectiveSafeArea.HorizontalThickness, crossPlatformSize.Height + effectiveSafeArea.VerticalThickness);
535+
crossPlatformSize = new Size(crossPlatformSize.Width + _safeArea.HorizontalThickness, crossPlatformSize.Height + _safeArea.VerticalThickness);
591536
}
592537

593538
return crossPlatformSize;
@@ -600,13 +545,21 @@ Size CrossPlatformMeasure(double widthConstraint, double heightConstraint)
600545
/// <param name="bounds">The bounds rectangle to arrange within</param>
601546
void CrossPlatformArrange(CGRect bounds)
602547
{
603-
if (_appliesSafeAreaAdjustments)
548+
// Force safe area revalidation for CollectionView cells when Window safe area changes.
549+
if (View is ISafeAreaView or ISafeAreaView2 && _collectionViewDescendant == true && Window is not null)
604550
{
605-
bounds = AdjustForSafeArea(bounds);
551+
var currentWindowPadding = Window.SafeAreaInsets.ToSafeAreaInsets();
552+
if (!currentWindowPadding.EqualsAtPixelLevel(_lastWindowSafeAreaPadding))
553+
{
554+
_lastWindowSafeAreaPadding = currentWindowPadding;
555+
_safeAreaInvalidated = true;
556+
ValidateSafeArea();
557+
}
606558
}
607-
else if (!CellSafeAreaOverride.IsEmpty)
559+
560+
if (_appliesSafeAreaAdjustments)
608561
{
609-
bounds = CellSafeAreaOverride.InsetRect(bounds);
562+
bounds = AdjustForSafeArea(bounds);
610563
}
611564

612565
CrossPlatformLayout?.CrossPlatformArrange(bounds.ToRectangle());
@@ -848,6 +801,8 @@ public override void MovedToWindow()
848801

849802
_scrollViewDescendant = null;
850803
_parentHandlesSafeArea = null;
804+
_collectionViewDescendant = null;
805+
_lastWindowSafeAreaPadding = SafeAreaPadding.Empty;
851806

852807
// Notify any subscribers that this view has been moved to a window
853808
_movedToWindow?.Invoke(this, EventArgs.Empty);

0 commit comments

Comments
 (0)