Skip to content
Merged
146 changes: 146 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue24977.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 24977, "Keyboard Scrolling in editors with Center or End VerticalTextAlignment is off", PlatformAffected.iOS)]
public class Issue24977 : TestNavigationPage
{
protected override void Init()
{
PushAsync(new ContentPage
{
Content = new VerticalStackLayout
{
Spacing = 12,
Padding = new Thickness(12, 24),
Children =
{
CreateButton(TextAlignment.Start),
CreateButton(TextAlignment.Center),
CreateButton(TextAlignment.End),
}
}
});
}

Button CreateButton(TextAlignment textAlignment)
{
var btn = new Button
{
Text = textAlignment.ToString(),
AutomationId = $"{textAlignment}Button"
};
btn.Clicked += (s, e) => Navigation.PushAsync(new Issue24977_1(textAlignment));
return btn;
}
}

public class Issue24977_1 : TestContentPage
{
Editor editor;
Label cursorHeightTracker;

public Issue24977_1(TextAlignment textAlignment)
{
editor.VerticalTextAlignment = textAlignment;
}

protected override void Init()
{
var rootGrid = new Grid
{
RowDefinitions =
{
new RowDefinition { Height = 50 },
new RowDefinition { Height = 50 },
new RowDefinition { Height = new GridLength(1, GridUnitType.Star) },
new RowDefinition { Height = 50 }
},
Margin = new Thickness(30)
};

var entry = new Entry
{
Text = "Content before",
AutomationId = "EntryBefore",
ReturnType = ReturnType.Next,
BackgroundColor = Colors.Aquamarine,
};

cursorHeightTracker = new Label
{
Text = "0",
AutomationId = "CursorHeightTracker",
BackgroundColor = Colors.Aquamarine,
};

editor = new Editor
{
Text = "Hello World!",
AutomationId = "IssueEditor",
BackgroundColor = Colors.Orange,
};
editor.TextChanged += Editor_TextChanged;

var button = new Button { Text = "Erase" };
button.Clicked += (s, e) => editor.Text = string.Empty;

rootGrid.Add(entry, 0, 0);
rootGrid.Add(cursorHeightTracker, 0, 1);
rootGrid.Add(editor, 0, 2);

rootGrid.Add(button, 0, 3);

Content = rootGrid;
}

private void Editor_TextChanged(object sender, TextChangedEventArgs e)
{
if (sender is Editor editor)
{
AddCursorHeightToLabel(editor);
}
}

void AddCursorHeightToLabel(Editor editor)
{
#if IOS
var textInput = editor.Handler.PlatformView as UIKit.UITextView;
var selectedTextRange = textInput?.SelectedTextRange;
var localCursor = selectedTextRange is not null ? textInput?.GetCaretRectForPosition(selectedTextRange.Start) : null;

if (localCursor is CoreGraphics.CGRect local && textInput is not null)
{
var container = GetContainerView(textInput);
var cursorInContainer = container.ConvertRectFromView(local, textInput);
var cursorInWindow = container.ConvertRectToView(cursorInContainer, null);

cursorHeightTracker.Text = cursorInWindow.Y.ToString();
}

UIKit.UIView GetContainerView(UIKit.UIView startingPoint)
{
var rootView = FindResponder<Microsoft.Maui.Platform.ContainerViewController>(startingPoint)?.View;

if (rootView is not null)
{
return rootView;
}

return null;
}

T FindResponder<T>(UIKit.UIView view)
where T : UIKit.UIResponder
{
var nextResponder = view as UIKit.UIResponder;
while (nextResponder is not null)
{
nextResponder = nextResponder.NextResponder;

if (nextResponder is T responder)
return responder;
}
return null;
}
#endif
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#if IOS
using System.Drawing;
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
using System.Text;

namespace Microsoft.Maui.TestCases.Tests.Issues;
public class Issue24977 : _IssuesUITest
{
public Issue24977(TestDevice device) : base(device) { }

public override string Issue => "Keyboard Scrolling in editors with Center or End VerticalTextAlignment is off";

[Test]
[Category(UITestCategories.Editor)]
public void KeepEditorCursorAboveKeyboardWithVerticalAlignmentAsCenter()
{
TestKeepEditorCursorAboveKeyboard("CenterButton", true);
}

[Test]
[Category(UITestCategories.Editor)]
public void KeepEditorCursorAboveKeyboardWithVerticalAlignmentAsEnd()
{
TestKeepEditorCursorAboveKeyboard("EndButton");
}

void TestKeepEditorCursorAboveKeyboard(string buttonId, bool fromCenter = false)
{
App.WaitForElement(buttonId);
App.Tap(buttonId);
Thread.Sleep(1000);

var app = App as AppiumApp;
if (app == null)
return;

var editorRect = app.WaitForElement("IssueEditor").GetRect();
app.Click("IssueEditor");

var sb = new StringBuilder();
for (int i = 1; i <= 20; i++)
{
sb.Append($"\n{i}");
}

app.EnterText("IssueEditor", sb.ToString());

var keyboardLocation = KeyboardScrolling.FindiOSKeyboardLocation(app.Driver);

var cursorLabel = app.WaitForElement("CursorHeightTracker").GetText();
var cursorHeight1 = Convert.ToDouble(cursorLabel);
try
{
if (keyboardLocation is Point keyboardPoint)
{
Assert.That(cursorHeight1 < keyboardPoint.Y);
}
else
{
Assert.Fail("keyboardLocation is null");
}
}
finally
{
if (fromCenter)
{
App.Back();
}
}
}
}
#endif
2 changes: 1 addition & 1 deletion src/Core/src/Platform/iOS/KeyboardAutoManagerScroll.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static class KeyboardAutoManagerScroll
static CGPoint StartingContentOffset;
static UIEdgeInsets StartingScrollIndicatorInsets;
static UIEdgeInsets StartingContentInsets;
static CGRect KeyboardFrame = CGRect.Empty;
internal static CGRect KeyboardFrame = CGRect.Empty;
static CGPoint TopViewBeginOrigin = new(nfloat.MaxValue, nfloat.MaxValue);
static readonly CGPoint InvalidPoint = new(nfloat.MaxValue, nfloat.MaxValue);
static double AnimationDuration = 0.25;
Expand Down
13 changes: 13 additions & 0 deletions src/Core/src/Platform/iOS/MauiTextView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,19 @@ void ShouldCenterVertically()
Maui.TextAlignment.End => new CGPoint(0, -Math.Max(1, availableSpace)),
_ => ContentOffset,
};

// Scroll the content to the cursor position if it is hidden by the keyboard
if (KeyboardAutoManagerScroll.IsKeyboardShowing && (VerticalTextAlignment == Maui.TextAlignment.Center || VerticalTextAlignment == Maui.TextAlignment.End))
{
var cursorRect = KeyboardAutoManagerScroll.FindCursorPosition();
var keyboardTop = KeyboardAutoManagerScroll.KeyboardFrame.Top;

if (cursorRect.HasValue && cursorRect.Value.Bottom > keyboardTop)
{
var offset = cursorRect.Value.Bottom - KeyboardAutoManagerScroll.KeyboardFrame.Top;
ContentOffset = new CGPoint(ContentOffset.X, ContentOffset.Y + offset);
}
}
}

void UpdatePlaceholderFont(UIFont? value)
Expand Down
Loading