Skip to content

Commit b57ca5f

Browse files
committed
Fix iOS ScrollView content being measured with non-infinite constraints during LayoutSubviews pass
1 parent 5ceff42 commit b57ca5f

File tree

4 files changed

+53
-9
lines changed

4 files changed

+53
-9
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace Controls.TestCases.HostApp.Issues;
2+
3+
[Issue(IssueTracker.Github, 27169, "Grid inside ScrollView should measure with infinite constraints", PlatformAffected.iOS)]
4+
public class Issue27169 : ContentPage
5+
{
6+
public Issue27169()
7+
{
8+
var grid = new Grid { VerticalOptions = LayoutOptions.Start };
9+
grid.RowDefinitions.Add(new RowDefinition(GridLength.Star));
10+
grid.RowDefinitions.Add(new RowDefinition(GridLength.Star));
11+
var firstContent = new Label { MinimumHeightRequest = 200, AutomationId = "StubLabel", BackgroundColor = Colors.LightBlue };
12+
firstContent.SetBinding(Label.TextProperty, new Binding(nameof(Label.Height), source: firstContent));
13+
var secondContent = new Label { MinimumHeightRequest = 40, Text = "Second Content", BackgroundColor = Colors.SlateBlue };
14+
15+
grid.Add(firstContent);
16+
grid.Add(secondContent, 0 , 1);
17+
18+
var scrollView = new ScrollView { Content = grid };
19+
Content = scrollView;
20+
}
21+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using NUnit.Framework;
2+
using NUnit.Framework.Legacy;
3+
using UITest.Appium;
4+
using UITest.Core;
5+
6+
namespace Microsoft.Maui.TestCases.Tests.Issues
7+
{
8+
public class Issue27169(TestDevice device) : _IssuesUITest(device)
9+
{
10+
public override string Issue => "Grid inside ScrollView should measure with infinite constraints";
11+
12+
[Test]
13+
[Category(UITestCategories.ScrollView)]
14+
public void ScrollViewContentLayoutMeasuresWithInfiniteConstraints()
15+
{
16+
var measuredHeight = App.WaitForElement("StubLabel").GetText()!;
17+
ClassicAssert.AreEqual("200", measuredHeight);
18+
}
19+
}
20+
}

src/Core/src/Platform/iOS/MauiScrollView.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@ public override void LayoutSubviews()
3636
_lastArrangeWidth = widthConstraint;
3737
_lastArrangeHeight = heightConstraint;
3838

39-
// If the SuperView is a MauiView (backing a cross-platform ContentView or Layout), then measurement
40-
// has already happened via SizeThatFits and doesn't need to be repeated in LayoutSubviews. But we
41-
// _do_ need LayoutSubviews to make a measurement pass if the parent is something else (for example,
39+
// If the SuperView is a MauiView or MauiScrollView (backing a cross-platform ContentView or Layout), then measurement
40+
// has already happened via SizeThatFits and doesn't need to be repeated in LayoutSubviews.
41+
// This is especially important to avoid overriding potentially infinite measurement constraints
42+
// imposed by the parent (i.e. scroll view) with the current bounds.
43+
// But we _do_ need LayoutSubviews to make a measurement pass if the parent is something else (for example,
4244
// the window); there's no guarantee that SizeThatFits has been called in that case.
43-
if (!IsMeasureValid(widthConstraint, heightConstraint) && Superview is not MauiView)
45+
if (!IsMeasureValid(widthConstraint, heightConstraint) && Superview is not (MauiView or MauiScrollView))
4446
{
4547
crossPlatformLayout.CrossPlatformMeasure(widthConstraint, heightConstraint);
4648
CacheMeasureConstraints(widthConstraint, heightConstraint);

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,13 @@ public override void LayoutSubviews()
125125
var widthConstraint = bounds.Width;
126126
var heightConstraint = bounds.Height;
127127

128-
// If the SuperView is a MauiView (backing a cross-platform ContentView or Layout), then measurement
129-
// has already happened via SizeThatFits and doesn't need to be repeated in LayoutSubviews. But we
130-
// _do_ need LayoutSubviews to make a measurement pass if the parent is something else (for example,
128+
// If the SuperView is a MauiView or MauiScrollView (backing a cross-platform ContentView or Layout), then measurement
129+
// has already happened via SizeThatFits and doesn't need to be repeated in LayoutSubviews.
130+
// This is especially important to avoid overriding potentially infinite measurement constraints
131+
// imposed by the parent (i.e. scroll view) with the current bounds.
132+
// But we _do_ need LayoutSubviews to make a measurement pass if the parent is something else (for example,
131133
// the window); there's no guarantee that SizeThatFits has been called in that case.
132-
133-
if (!IsMeasureValid(widthConstraint, heightConstraint) && Superview is not MauiView)
134+
if (!IsMeasureValid(widthConstraint, heightConstraint) && Superview is not (MauiView or MauiScrollView))
134135
{
135136
CrossPlatformMeasure(widthConstraint, heightConstraint);
136137
CacheMeasureConstraints(widthConstraint, heightConstraint);

0 commit comments

Comments
 (0)