Skip to content

Commit

Permalink
Improve keyboard scrolling with editors, contentInsets, and different…
Browse files Browse the repository at this point in the history
… keyboards (#24589)

* Changes to improve keyboard scrolling with editors, contentInsets, and different keyboards

* picker6 screenshot

* Add entry7 screenshot test

* reset test state

* fix the distance between keyboard and cursor and remove null forgiving

* bottomBoundary should have the TextDistanceFromBottom when the bottom is not the keyboard

* FireAndForget

* new entry7 screenshot

* add back the distance from bottom in the insets

* Allow partial scrolling one direction and then moving the next scrollview the opposite direction

* Adjust insets on UITextView and other scrollviews

* we dont need more inset if no keyboard intersection

* add edge case where we have small editor
  • Loading branch information
tj-devel709 committed Sep 19, 2024
1 parent 60076be commit 14c7719
Show file tree
Hide file tree
Showing 23 changed files with 1,133 additions and 102 deletions.
125 changes: 125 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue19214.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue19214"
Title="Issue19214">
<Grid RowDefinitions="40,*,*,*" Margin="20">
<ScrollView Background="LightBlue" Padding="10" Grid.Row="1" AutomationId="ScrollView_1">
<VerticalStackLayout>
<Entry Text="Top Scroll" Background="Lightgray" HeightRequest="40" AutomationId="TopEntry_1"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry1" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry2" Background="Lightgray" HeightRequest="40" AutomationId="Entry2_1"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry3" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry4" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry5" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry6" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry7" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry8" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry9" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry10" Background="Lightgray" HeightRequest="40" AutomationId="Entry10_1"/>
<BoxView HeightRequest="40" />

<Entry Text="Bottom!!" Background="Green" HeightRequest="40" AutomationId="BottomEntry_1"/>
</VerticalStackLayout>
</ScrollView>

<ScrollView Background="Lightgreen" Padding="10" Grid.Row="2" AutomationId="ScrollView_2">
<VerticalStackLayout>
<Entry Text="Top Scroll" Background="Lightgray" HeightRequest="40" AutomationId="TopEntry_2"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry1" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry2" Background="Lightgray" HeightRequest="40" AutomationId="Entry2_2"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry3" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry4" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry5" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry6" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry7" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry8" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry9" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry10" Background="Lightgray" HeightRequest="40" AutomationId="Entry10_2"/>
<BoxView HeightRequest="40" />

<Entry Text="Bottom!!" Background="Green" HeightRequest="40" AutomationId="BottomEntry_2"/>
</VerticalStackLayout>
</ScrollView>


<ScrollView Background="pink" Padding="10" Grid.Row="3" AutomationId="ScrollView_3">
<VerticalStackLayout>
<Entry Text="Top Scroll" Background="Lightgray" HeightRequest="40" AutomationId="TopEntry_3"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry1" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry2" Background="Lightgray" HeightRequest="40" AutomationId="Entry2_3"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry3" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry4" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry5" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry6" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry7" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry8" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry9" Background="Lightgray" HeightRequest="40"/>
<BoxView HeightRequest="40" />

<Entry Text="Entry10" Background="Lightgray" HeightRequest="40" AutomationId="Entry10_3"/>
<BoxView HeightRequest="40" />

<Entry Text="Bottom!!" Background="Green" HeightRequest="40" AutomationId="BottomEntry_3"/>
</VerticalStackLayout>
</ScrollView>
</Grid>
</ContentPage>
15 changes: 15 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue19214.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Xaml;

namespace Maui.Controls.Sample.Issues;

[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 19214, "iOS Keyboard Scrolling ContentInset Tests", PlatformAffected.iOS)]
public partial class Issue19214 : ContentPage
{
public Issue19214()
{
InitializeComponent();
}
}
12 changes: 12 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue19214_2.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue19214_2"
Title="Issue19214_2">
<Grid RowDefinitions="50, 50, *, 50" Margin="30">
<Entry Text="Content before" AutomationId="EntryBefore" FontSize="Large" ReturnType="Next" BackgroundColor="Aquamarine" Grid.Row="0" />
<Label x:Name="CursorHeightTracker" Text="0" AutomationId="CursorHeightTracker" FontSize="Large" BackgroundColor="Aquamarine" Grid.Row="1" />
<Editor x:Name="editor" AutomationId="IssueEditor" FontSize="Large" BackgroundColor="Orange" Grid.Row="2" TextChanged="Editor_TextChanged" />
<Button Text="Erase" Clicked="Button_Clicked" Grid.Row="3"/>
</Grid>
</ContentPage>
72 changes: 72 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue19214_2.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Xaml;

namespace Maui.Controls.Sample.Issues;

[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 19214_2, "iOS Editor Cursor stays above keyboard - Top level Grid", PlatformAffected.iOS)]
public partial class Issue19214_2 : ContentPage
{
public Issue19214_2()
{
InitializeComponent();
}

private void Button_Clicked(object sender, EventArgs e)
{
editor.Text = string.Empty;
}

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
}
}
14 changes: 14 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue19214_3.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue19214_3"
Title="Issue19214_3">
<ScrollView Margin="30">
<VerticalStackLayout>
<Entry Text="Content before" AutomationId="EntryBefore" FontSize="Large" ReturnType="Next" BackgroundColor="Aquamarine"/>
<Label x:Name="CursorHeightTracker" Text="0" AutomationId="CursorHeightTracker" FontSize="Large" BackgroundColor="Aquamarine" />
<Editor x:Name="editor" HeightRequest="650" AutomationId="IssueEditor" FontSize="Large" BackgroundColor="Orange" TextChanged="Editor_TextChanged" />
<Button Text="Erase" Clicked="Button_Clicked" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>
72 changes: 72 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue19214_3.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Xaml;

namespace Maui.Controls.Sample.Issues;

[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 19214_3, "iOS Editor Cursor stays above keyboard - Top level ScrollView", PlatformAffected.iOS)]
public partial class Issue19214_3 : ContentPage
{
public Issue19214_3()
{
InitializeComponent();
}

private void Button_Clicked(object sender, EventArgs e)
{
editor.Text = string.Empty;
}

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
}
}
40 changes: 40 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue22715.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue22715"
Title="Issue22715"
Loaded="OnPageLoaded"
AutomationId="contentPage">

<ScrollView>
<VerticalStackLayout>

<Grid
RowDefinitions="Auto, Auto, Auto"
ColumnDefinitions="300">

<Border Grid.Row="0" StrokeThickness="2" Stroke="Black" >
<Label
AutomationId="TopLabel"
BackgroundColor="LightGray"
HeightRequest="50"
TextColor="Black"
FontSize="20"
Text="Enter a number" />
</Border>

<Border Grid.Row="1" StrokeThickness="2" Stroke="Black" >
<Entry
x:Name="EntNumber"
AutomationId="EntNumber"
BackgroundColor="LightBlue"
Keyboard="Numeric"
ReturnType="Next"
HorizontalOptions="Fill"
Focused="EntNumber_Focused"
TextColor="Black" />
</Border>
</Grid>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
32 changes: 32 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue22715.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Xaml;

namespace Maui.Controls.Sample.Issues;

[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 22715, "Page should not scroll when focusing element above keyboard", PlatformAffected.iOS)]
public partial class Issue22715 : ContentPage
{
public Issue22715()
{
InitializeComponent();
}

private void OnPageLoaded(object sender, EventArgs e)
{
EntNumber.Focus();
}

void EntNumber_Focused(object sender, FocusEventArgs e)
{
#if IOS
var entry = (Entry)sender;
var field = entry.Handler?.PlatformView as UIKit.UITextField;
if (field is not null)
{
field.TintColor = UIKit.UIColor.Clear;
}
#endif
}
}
Loading

0 comments on commit 14c7719

Please sign in to comment.