Skip to content

Commit

Permalink
Fix SafeArea adjustments
Browse files Browse the repository at this point in the history
Co-Authored-By: Shane Neuville <[email protected]>
  • Loading branch information
kubaflo and PureWeen committed Nov 14, 2024
1 parent c1bb5fd commit a17b2d1
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 1 deletion.
10 changes: 10 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue24246.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<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 x:Name="layout" Background="Purple" IgnoreSafeArea="False" VerticalOptions="Start" IsClippedToBounds="True" >
<Entry x:Name="label" Background="Green" Text="Hello there"></Entry>
</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!"));
}
}
}
2 changes: 2 additions & 0 deletions src/Core/src/Platform/iOS/MauiScrollView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public class MauiScrollView : UIScrollView, IUIViewLifeCycleEvents
{
public MauiScrollView()
{
// By setting this to 'Never', we take full control of the content layout and manage the insets manually in our code, avoiding any automatic adjustments by the system.
ContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentBehavior.Never;
}

// overriding this method so it does not automatically scroll large UITextFields
Expand Down
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,6 +26,11 @@ 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")));
Expand All @@ -38,7 +43,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 +96,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 +116,27 @@ 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;

CacheMeasureConstraints(widthConstraint + widthSafeAreaOffset, heightConstraint + heightSafeAreaOffset);

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

0 comments on commit a17b2d1

Please sign in to comment.