Skip to content

Commit 9784176

Browse files
NirmalKumarYuvarajmattleibowDhivya-SF4094
authored andcommitted
Fixed Keyboard Scrolling in editors with Center or End VerticalTextAlignment (#25827)
* Fixed Keyboard scrolling in editors with Center or end VerticalTextAlignment is off * Modified the code change to consider content offset value * Removed unwanted file changes * Removed test cases * Modified changes * Modified the code changes and included UI test * Removed unwanted code changes * updated changes * optimized changes * updated test case * Added a button for Start * Changes in MauiTextView --------- Co-authored-by: Matthew Leibowitz <[email protected]> Co-authored-by: Dhivya-SF4094 <[email protected]>
1 parent 97251c0 commit 9784176

File tree

4 files changed

+234
-1
lines changed

4 files changed

+234
-1
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
namespace Maui.Controls.Sample.Issues;
2+
3+
[Issue(IssueTracker.Github, 24977, "Keyboard Scrolling in editors with Center or End VerticalTextAlignment is off", PlatformAffected.iOS)]
4+
public class Issue24977 : TestNavigationPage
5+
{
6+
protected override void Init()
7+
{
8+
PushAsync(new ContentPage
9+
{
10+
Content = new VerticalStackLayout
11+
{
12+
Spacing = 12,
13+
Padding = new Thickness(12, 24),
14+
Children =
15+
{
16+
CreateButton(TextAlignment.Start),
17+
CreateButton(TextAlignment.Center),
18+
CreateButton(TextAlignment.End),
19+
}
20+
}
21+
});
22+
}
23+
24+
Button CreateButton(TextAlignment textAlignment)
25+
{
26+
var btn = new Button
27+
{
28+
Text = textAlignment.ToString(),
29+
AutomationId = $"{textAlignment}Button"
30+
};
31+
btn.Clicked += (s, e) => Navigation.PushAsync(new Issue24977_1(textAlignment));
32+
return btn;
33+
}
34+
}
35+
36+
public class Issue24977_1 : TestContentPage
37+
{
38+
Editor editor;
39+
Label cursorHeightTracker;
40+
41+
public Issue24977_1(TextAlignment textAlignment)
42+
{
43+
editor.VerticalTextAlignment = textAlignment;
44+
}
45+
46+
protected override void Init()
47+
{
48+
var rootGrid = new Grid
49+
{
50+
RowDefinitions =
51+
{
52+
new RowDefinition { Height = 50 },
53+
new RowDefinition { Height = 50 },
54+
new RowDefinition { Height = new GridLength(1, GridUnitType.Star) },
55+
new RowDefinition { Height = 50 }
56+
},
57+
Margin = new Thickness(30)
58+
};
59+
60+
var entry = new Entry
61+
{
62+
Text = "Content before",
63+
AutomationId = "EntryBefore",
64+
ReturnType = ReturnType.Next,
65+
BackgroundColor = Colors.Aquamarine,
66+
};
67+
68+
cursorHeightTracker = new Label
69+
{
70+
Text = "0",
71+
AutomationId = "CursorHeightTracker",
72+
BackgroundColor = Colors.Aquamarine,
73+
};
74+
75+
editor = new Editor
76+
{
77+
Text = "Hello World!",
78+
AutomationId = "IssueEditor",
79+
BackgroundColor = Colors.Orange,
80+
};
81+
editor.TextChanged += Editor_TextChanged;
82+
83+
var button = new Button { Text = "Erase" };
84+
button.Clicked += (s, e) => editor.Text = string.Empty;
85+
86+
rootGrid.Add(entry, 0, 0);
87+
rootGrid.Add(cursorHeightTracker, 0, 1);
88+
rootGrid.Add(editor, 0, 2);
89+
90+
rootGrid.Add(button, 0, 3);
91+
92+
Content = rootGrid;
93+
}
94+
95+
private void Editor_TextChanged(object sender, TextChangedEventArgs e)
96+
{
97+
if (sender is Editor editor)
98+
{
99+
AddCursorHeightToLabel(editor);
100+
}
101+
}
102+
103+
void AddCursorHeightToLabel(Editor editor)
104+
{
105+
#if IOS
106+
var textInput = editor.Handler.PlatformView as UIKit.UITextView;
107+
var selectedTextRange = textInput?.SelectedTextRange;
108+
var localCursor = selectedTextRange is not null ? textInput?.GetCaretRectForPosition(selectedTextRange.Start) : null;
109+
110+
if (localCursor is CoreGraphics.CGRect local && textInput is not null)
111+
{
112+
var container = GetContainerView(textInput);
113+
var cursorInContainer = container.ConvertRectFromView(local, textInput);
114+
var cursorInWindow = container.ConvertRectToView(cursorInContainer, null);
115+
116+
cursorHeightTracker.Text = cursorInWindow.Y.ToString();
117+
}
118+
119+
UIKit.UIView GetContainerView(UIKit.UIView startingPoint)
120+
{
121+
var rootView = FindResponder<Microsoft.Maui.Platform.ContainerViewController>(startingPoint)?.View;
122+
123+
if (rootView is not null)
124+
{
125+
return rootView;
126+
}
127+
128+
return null;
129+
}
130+
131+
T FindResponder<T>(UIKit.UIView view)
132+
where T : UIKit.UIResponder
133+
{
134+
var nextResponder = view as UIKit.UIResponder;
135+
while (nextResponder is not null)
136+
{
137+
nextResponder = nextResponder.NextResponder;
138+
139+
if (nextResponder is T responder)
140+
return responder;
141+
}
142+
return null;
143+
}
144+
#endif
145+
}
146+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#if IOS
2+
using System.Drawing;
3+
using NUnit.Framework;
4+
using UITest.Appium;
5+
using UITest.Core;
6+
using System.Text;
7+
8+
namespace Microsoft.Maui.TestCases.Tests.Issues;
9+
public class Issue24977 : _IssuesUITest
10+
{
11+
public Issue24977(TestDevice device) : base(device) { }
12+
13+
public override string Issue => "Keyboard Scrolling in editors with Center or End VerticalTextAlignment is off";
14+
15+
[Test]
16+
[Category(UITestCategories.Editor)]
17+
public void KeepEditorCursorAboveKeyboardWithVerticalAlignmentAsCenter()
18+
{
19+
TestKeepEditorCursorAboveKeyboard("CenterButton", true);
20+
}
21+
22+
[Test]
23+
[Category(UITestCategories.Editor)]
24+
public void KeepEditorCursorAboveKeyboardWithVerticalAlignmentAsEnd()
25+
{
26+
TestKeepEditorCursorAboveKeyboard("EndButton");
27+
}
28+
29+
void TestKeepEditorCursorAboveKeyboard(string buttonId, bool fromCenter = false)
30+
{
31+
App.WaitForElement(buttonId);
32+
App.Tap(buttonId);
33+
Thread.Sleep(1000);
34+
35+
var app = App as AppiumApp;
36+
if (app == null)
37+
return;
38+
39+
var editorRect = app.WaitForElement("IssueEditor").GetRect();
40+
app.Click("IssueEditor");
41+
42+
var sb = new StringBuilder();
43+
for (int i = 1; i <= 20; i++)
44+
{
45+
sb.Append($"\n{i}");
46+
}
47+
48+
app.EnterText("IssueEditor", sb.ToString());
49+
50+
var keyboardLocation = KeyboardScrolling.FindiOSKeyboardLocation(app.Driver);
51+
52+
var cursorLabel = app.WaitForElement("CursorHeightTracker").GetText();
53+
var cursorHeight1 = Convert.ToDouble(cursorLabel);
54+
try
55+
{
56+
if (keyboardLocation is Point keyboardPoint)
57+
{
58+
Assert.That(cursorHeight1 < keyboardPoint.Y);
59+
}
60+
else
61+
{
62+
Assert.Fail("keyboardLocation is null");
63+
}
64+
}
65+
finally
66+
{
67+
if (fromCenter)
68+
{
69+
App.Back();
70+
}
71+
}
72+
}
73+
}
74+
#endif

src/Core/src/Platform/iOS/KeyboardAutoManagerScroll.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public static class KeyboardAutoManagerScroll
2424
static CGPoint StartingContentOffset;
2525
static UIEdgeInsets StartingScrollIndicatorInsets;
2626
static UIEdgeInsets StartingContentInsets;
27-
static CGRect KeyboardFrame = CGRect.Empty;
27+
internal static CGRect KeyboardFrame = CGRect.Empty;
2828
static CGPoint TopViewBeginOrigin = new(nfloat.MaxValue, nfloat.MaxValue);
2929
static readonly CGPoint InvalidPoint = new(nfloat.MaxValue, nfloat.MaxValue);
3030
static double AnimationDuration = 0.25;

src/Core/src/Platform/iOS/MauiTextView.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,19 @@ void ShouldCenterVertically()
172172
Maui.TextAlignment.End => new CGPoint(0, -Math.Max(1, availableSpace)),
173173
_ => ContentOffset,
174174
};
175+
176+
// Scroll the content to the cursor position if it is hidden by the keyboard
177+
if (KeyboardAutoManagerScroll.IsKeyboardShowing && (VerticalTextAlignment == Maui.TextAlignment.Center || VerticalTextAlignment == Maui.TextAlignment.End))
178+
{
179+
var cursorRect = KeyboardAutoManagerScroll.FindCursorPosition();
180+
var keyboardTop = KeyboardAutoManagerScroll.KeyboardFrame.Top;
181+
182+
if (cursorRect.HasValue && cursorRect.Value.Bottom > keyboardTop)
183+
{
184+
var offset = cursorRect.Value.Bottom - KeyboardAutoManagerScroll.KeyboardFrame.Top;
185+
ContentOffset = new CGPoint(ContentOffset.X, ContentOffset.Y + offset);
186+
}
187+
}
175188
}
176189

177190
void UpdatePlaceholderFont(UIFont? value)

0 commit comments

Comments
 (0)