Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix SafeArea adjustments #27043

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue24246.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue24246"
Title="Issue24246"
xmlns:ios="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;assembly=Microsoft.Maui.Controls"
ios:Page.UseSafeArea="false">
<VerticalStackLayout Background="Purple" IgnoreSafeArea="False" VerticalOptions="Start" IsClippedToBounds="True" >
<Entry AutomationId="entry" Background="Green"></Entry>
<Label AutomationId="label" Text="If you can't interact with the above entry field, the test has failed" LineBreakMode="CharacterWrap"></Label>
</VerticalStackLayout>
</ContentPage>
11 changes: 11 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue24246.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Maui.Controls.Sample.Issues;


[Issue(IssueTracker.Github, 24246, "SafeArea arrange insets are currently insetting based on an incorrect Bounds", PlatformAffected.iOS)]
public partial class Issue24246 : ContentPage
{
public Issue24246()
{
InitializeComponent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class Issue24246 : _IssuesUITest
{
public Issue24246(TestDevice testDevice) : base(testDevice)
{
}

public override string Issue => "SafeArea arrange insets are currently insetting based on an incorrect Bounds";

[Test]
[Category(UITestCategories.Layout)]
public void TapThenDoubleTap()
{
App.WaitForElement("entry");
App.EnterText("entry", "Hello, World!");

var result = App.WaitForElement("entry").GetText();
Assert.That(result, Is.EqualTo("Hello, World!"));
}
}
}
34 changes: 33 additions & 1 deletion src/Core/src/Platform/iOS/MauiView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,16 @@ public IView? View

bool RespondsToSafeArea()
{
if (View is not ISafeAreaView sav || sav.IgnoreSafeArea)
{
return false;
}

if (_respondsToSafeArea.HasValue)
return _respondsToSafeArea.Value;

return (bool)(_respondsToSafeArea = RespondsToSelector(new Selector("safeAreaInsets")));

}

protected CGRect AdjustForSafeArea(CGRect bounds)
Expand All @@ -38,7 +45,7 @@ protected CGRect AdjustForSafeArea(CGRect bounds)
KeyboardAutoManagerScroll.ShouldScrollAgain = true;
}

if (View is not ISafeAreaView sav || sav.IgnoreSafeArea || !RespondsToSafeArea())
if (!RespondsToSafeArea())
{
return bounds;
}
Expand Down Expand Up @@ -91,6 +98,12 @@ Size CrossPlatformArrange(Rect bounds)
return CrossPlatformLayout?.CrossPlatformArrange(bounds) ?? Size.Zero;
}

// SizeThatFits does not take into account the constraints set on the view.
// For example, if the user has set a width and height on this view, those constraints
// will not be reflected in the value returned from this method. This method purely returns
// a measure based on the size that is passed in.
// The constraints are all applied by ViewHandlerExtensions.GetDesiredSizeFromHandler
// after it calls this method.
public override CGSize SizeThatFits(CGSize size)
{
if (_crossPlatformLayoutReference == null)
Expand All @@ -105,6 +118,25 @@ public override CGSize SizeThatFits(CGSize size)

CacheMeasureConstraints(widthConstraint, heightConstraint);

// If for some reason the upstream measure passes in a negative contraint
// Lets just bypass this code
if (RespondsToSafeArea() && widthConstraint >= 0 && heightConstraint >= 0)
{
// During the LayoutSubViews pass, we adjust the Bounds of this view for the safe area and then pass the adjusted result to CrossPlatformArrange.
// The CrossPlatformMeasure call does not include the safe area, so we need to add it here to ensure the returned size is correct.
//
// For example, if this is a layout with an Entry of height 20, CrossPlatformMeasure will return a height of 20.
// This means the bounds will be set to a height of 20, causing AdjustForSafeArea(Bounds) to return a negative bounds once it has
// subtracted the safe area insets. Therefore, we need to add the safe area insets to the CrossPlatformMeasure result to ensure correct arrangement.
var widthSafeAreaOffset = SafeAreaInsets.Left + SafeAreaInsets.Right;
var heightSafeAreaOffset = SafeAreaInsets.Top + SafeAreaInsets.Bottom;

var width = double.Clamp(crossPlatformSize.Width + widthSafeAreaOffset, 0, widthConstraint);
var height = double.Clamp(crossPlatformSize.Height + heightSafeAreaOffset, 0, heightConstraint);

return new CGSize(width, height);
}

return crossPlatformSize.ToCGSize();
}

Expand Down
Loading