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

[iOS] Editor autoscrolls its text view completely off the screen if you press 'return' too many times, has no constraint against scrolling the text view off screen. Objects lag in resizing around it. #17757

Open
jonmdev opened this issue Sep 30, 2023 · 13 comments
Labels
area-controls-editor Editor p/2 Work that is important, but is currently not scheduled for release partner/cat 😻 this is an issue that impacts one of our partners or a customer our advisory team is engaged with platform/iOS 🍎 s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working

Comments

@jonmdev
Copy link

jonmdev commented Sep 30, 2023

Description

A working Editor is essential for text input in a cross platform and mobile environment. Editor can be used as a field for users to type into such as in this forum box I am typing into now or a WhatsApp or SMS style text entry field.

A typical such Editor function is described by:

            Editor editor = new();
            editor.AutoSize = EditorAutoSizeOption.TextChanges;
            editor.MaximumHeightRequest = 200;

In Windows and Android, Editor functions properly with such a setting. However in iOS it is quite broken, exhibiting at least three problems:

  1. Editor in iOS will not scroll down automatically as you add new lines once it is at its max size (in Windows and Android, as you add more lines, the Editor automatically scrolls down to show them).

  2. Editor in iOS does not respond to touch and drag to scroll manually (in Android, you can touch and drag to scroll and Windows use the mouse scroll wheel to scroll).

  3. Parent element of Editor in iOS resizes with a one frame lag behind the Editor as the Editor expands, so as you add new lines and Editor resizes, Editor will momentarily overlap its parent object each time (in Android and Windows the resizing of parents are instant).

Android and Windows exhibit all the obvious desirable behaviors.

That is: (i) A text field must automatically scroll down to always show the current line of text being written to, (ii) touching and dragging is the only way to manually scroll a text field in mobile and must work as expected, and (iii) resizing of the Editor should not introduce visual glitches and resize lags.

These problems only exist in iOS. Behavior of Editor in Android and Windows appears perfect.

Steps to Reproduce

  1. Create a new blank MAUI project in Visual Studio 2022 using .NET 7.0 by File > New.

  2. Copy and paste the following to replace the default class in App.xaml.cs:

public partial class App : Application {
    public App() {
        InitializeComponent();

        MainPage = new ContentPage();

        VerticalStackLayout vert = new();
        (MainPage as ContentPage).Content = vert;
        
        Border border = new();
        border.StrokeThickness = 4;
        border.BackgroundColor = Colors.DarkBlue;
        border.Stroke = Colors.Red;
        border.Padding = 10;
        vert.Children.Add(border);

        Editor editor = new();
        editor.BackgroundColor = Colors.White;
        editor.AutoSize = EditorAutoSizeOption.TextChanges;
        editor.MaximumHeightRequest = 200;
        border.Content = editor;

        Label testLabel = new Label();
        testLabel.Text = "Editor iOS Bug Demonstration:\n- Editor does not autoscroll downward as you fill the Editor with text (new lines past max height go down off screen)\n- Editor does not scroll with click and drag once past maxHeight in size.\n- There is a frame lag after resizing Editor and before resizing the parent so the Editor goes 'out of bounds' of its parent momentarily each time it grows vertically.";
        vert.Children.Add(testLabel);

    }
}
  1. Run the project in Windows and Android and you will observe the following CORRECT BEHAVIOR:
  • Editor will automatically scroll down to show the newly added text if you keep adding new lines once it is at max height.
  • Editor scrolls properly from mouse scroll wheel function over the object in Windows, touch to drag in Android, and mouse scroll wheel over object if in Windows Android simulator.
  • Editor as well as its parent Border will resize instantly in Windows and Android with no visible lag so the blue border around the Editor remains uniform at all times.
  1. Run the project in iOS and observe by contrast the following WRONG BEHAVIOR:
  • Editor will not scroll down automatically as you add new lines once it is at max height.
  • Editor does not respond at all to touch and drag to scroll manually.
  • Border around Editor resizes with a one frame lag behind the Editor, so as you add new lines and the Editor expands, the Editor will momentarily overlap the blue background of Border at the bottom each time.

Expected behavior again is that Editor in iOS should show the same results as Windows and Android with: (i) automatic downward scrolling on new lines, (ii) manual scrolling on touch and drag (and scroll wheel if available from simulator), and (iii) instant resizing of its parents upon changing size.

Link to public reproduction project repository

https://github.com/jonmdev/Editor-iOS-Bug

Version with bug

7.0.92/8.0

Is this a regression from previous behavior?

Not sure, did not test other versions

Last version that worked well

Unknown/Other

Affected platforms

iOS

Affected platform versions

iOS 16.7 on iPhone XR with Debug build (Hot Restart)

Did you find any workaround?

None.

Relevant log output

No response

@jonmdev jonmdev added the t/bug Something isn't working label Sep 30, 2023
@jsuarezruiz jsuarezruiz added platform/iOS 🍎 area-controls-editor Editor s/try-latest-version Please try to reproduce the potential issue on the latest public version labels Oct 2, 2023
@ghost
Copy link

ghost commented Oct 2, 2023

Hi @jonmdev. We have added the "s/try-latest-version" label to this issue, which indicates that we'd like you to try and reproduce this issue on the latest available public version. This can happen because we think that this issue was fixed in a version that has just been released, or the information provided by you indicates that you might be working with an older version.

You can install the latest version by installing the latest Visual Studio (Preview) with the .NET MAUI workload installed. If the issue still persists, please let us know with any additional details and ideally a reproduction project provided through a GitHub repository.

This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

@tschbc
Copy link

tschbc commented Oct 4, 2023

[On MAUI 7.0.86 - iOS]

I've also experienced 1 & 2. In our app we've resorted to using a "go-to-bottom" button that moves your cursor and scrolls to the bottom the editor for you.

With some finagling we were able to get the editor somewhat working in a ScrollView—but it's still pretty broken. After <some amount> of lines have been entered, you can freely scroll. But before you hit that <some amount> of lines, you can only initiate a scroll by pulling down first.

@jonmdev
Copy link
Author

jonmdev commented Oct 6, 2023

I tried .NET 8.0 and it appears some of this has been fixed:

  1. It now does scroll down as you type.
  2. It now does respond to touch and drag.

However the issues are now:

  1. Still has a frame lag as you type new lines (with same project code above).
  2. Scrolling by touch and drag is not constrained, so you can slide it up all the way out of view.
  3. The new line auto-scrolling is glitchy - if you keep pressing enter it will keep moving the white text field higher and higher until it goes out of view.

Very, very strange.

But I suppose an improvement. At least we have touch/drag response of some kind. 👍

Any further help with fixing this?

To see the problem, open my code in .NET 8, build to iOS then start typing lots of new lines in the field. It will lag on resize, start pushing the text field up high out of view, and if you drag it you will find you can slide it up way out of bounds as well without anything to automatically stop it or bring it back into view after you release.

Thanks.

@ghost ghost removed the s/try-latest-version Please try to reproduce the potential issue on the latest public version label Oct 6, 2023
@jonmdev
Copy link
Author

jonmdev commented Oct 7, 2023

I posted a simple demo project of the code I posted above here if it is any help to show the issues:

https://github.com/jonmdev/Editor-iOS-Bug

@mikeparker104 mikeparker104 added the partner/cat 😻 this is an issue that impacts one of our partners or a customer our advisory team is engaged with label Oct 10, 2023
@samhouts samhouts added the p/1 Work that is important, and has been scheduled for release in this or an upcoming sprint label Oct 10, 2023
@samhouts samhouts changed the title Editor is severely broken on iOS: (1) Does not auto-scroll down as new lines are added past max height, (2) Does not respond to touch to manually scroll down/up, (3) Causes visible frame lag in resizing parents when it resizes [iOS] Editor causes visible frame lag in resizing parents when it resizes Oct 14, 2023
@samhouts samhouts added this to the .NET 8 SR2 milestone Oct 14, 2023
@Eilon Eilon added the legacy-area-controls Label, Button, CheckBox, Slider, Stepper, Switch, Picker, Entry, Editor label Oct 17, 2023
@jonmdev
Copy link
Author

jonmdev commented Oct 20, 2023

I see this is added to the .NET 8 SR2 milestone and title was changed of post to "Editor causes visible frame lag in resizing parents when it resizes".

I am happy it is being fixed. I hope the other issues of:

  • Scrolling by touch and drag is not constrained, so you can slide it up all the way out of view.
  • If you keep pressing enter it will keep moving the white text field higher and higher until it goes out of view.

Will also be solved.

I just checked and they are still reproduced with my demo project using 8.0.0-rc.2.9373 and it still demonstrates these bugs also.

To clarify, if you open the bug project, click in the touch field for Editor, and start pressing "return" repeatedly in iOS, it will start looking normal like this but eventually the white text field will slide completely off screen:

IMG_0006 IMG_0005 IMG_0007

Once the white field becomes big enough, you can also slide it unconstrained up and down to this degree (even all the way off screen).

I also hope we can have an option to disable being able to pull the text field past its edges at all so the touch/drag function matches better against Android.

Thanks again.

@tschbc
Copy link

tschbc commented Oct 20, 2023

@jonmdev I've had better luck using a StackLayout instead of VerticalStackLayout for most of my layout issues, so you may be interested in that as a workaround. (I know it's "less performant" but I'd personally rather have it actually work in the first place)

I can't share real xaml/code because my repo isn't public, but I can share the layout structure I use for my Editor:

<ContentPage>
<StackLayout Orientation="Vertical">

    <header views>

    <AbsoluteLayout VerticalOptions="FillAndExpand">
        <Editor
            AbsoluteLayout.LayoutFlags="All"
            AbsoluteLayout.LayoutBounds="0,0,1,1">

            <Editor.Behaviors>
                <toolkit:UserStoppedTypingBehavior />
            </Editor.Behaviors>
        </Editor>

        <ImageButton
            AbsoluteLayout.LayoutFlags="PositionProportional"
            AbsoluteLayout.LayoutBounds="1,1"
            WidthRequest="44"
            HeightRequest="44"
            CornerRadius="22"
            Margin="10"
            Padding="5">

            <ImageButton.Shadow>
                <Shadow />
            </ImageButton.Shadow>

            <ImageButton.Source>
                <FontImageSource />
            </ImageButton.Source>

        </ImageButton>
    </AbsoluteLayout>

</StackLayout>
</ContentPage>

The AbsoluteLayout is only there so I can float a button over top of the Editor. It may help with the layout issues, but I haven't tested without it, so I don't know. We used a Grid before switching to a StackLayout and left the AbsoluteLayout structure unchanged.

With this structure, and on MAUI 8 RC2, I've had zero issues with the Editor:

  • Editor correctly auto-scrolls when entering newlines past its bottom boundary
  • Editor correctly scrolls to cursor on TextChanged when cursor is outside its viewport
  • Editor correctly scrolls to cursor position when cursor position is set outside its viewport
  • Scrolling behaves as expected compared to other scrolling views like CollectionView

@jonmdev
Copy link
Author

jonmdev commented Oct 20, 2023

I have been able to identify that the resize lagging behavior for surrounding elements is more generalized and not specifically just due to Editor. I have created a separate repro and bug report then for that here that more easily demonstrates the problem:

#18204

As this is an Editor related bug thread, I will change the title back to reflect the Editor specific bugs as best I understand the current ones. Hopefully that's okay. Again, I hope all these bugs can be fixed as they are all making things quite dysfunctional and not very usable in iOS.

Perhaps all the bugs are actually the same thing? Ie. Maybe the same resize lag/glitch is what is causing the Editor to go off screen when you keep pressing 'return' as well? I am not sure. Maybe if we get that fixed it will fix everything?

@samhouts @Eilon

@jonmdev jonmdev changed the title [iOS] Editor causes visible frame lag in resizing parents when it resizes [iOS] Editor autoscrolls its text view completely off the screen if you press 'return' too many times, has no constraint against scrolling the text view off screen Oct 20, 2023
@jonmdev jonmdev changed the title [iOS] Editor autoscrolls its text view completely off the screen if you press 'return' too many times, has no constraint against scrolling the text view off screen [iOS] Editor autoscrolls its text view completely off the screen if you press 'return' too many times, has no constraint against scrolling the text view off screen. Objects lag in resizing around it. Oct 20, 2023
@jeff-eats-pubsubs
Copy link

jeff-eats-pubsubs commented Dec 8, 2023

@jonmdev I've had better luck using a StackLayout instead of VerticalStackLayout for most of my layout issues, so you may be interested in that as a workaround. (I know it's "less performant" but I'd personally rather have it actually work in the first place)

I can't share real xaml/code because my repo isn't public, but I can share the layout structure I use for my Editor:

<ContentPage>
<StackLayout Orientation="Vertical">

    <header views>

    <AbsoluteLayout VerticalOptions="FillAndExpand">
        <Editor
            AbsoluteLayout.LayoutFlags="All"
            AbsoluteLayout.LayoutBounds="0,0,1,1">

            <Editor.Behaviors>
                <toolkit:UserStoppedTypingBehavior />
            </Editor.Behaviors>
        </Editor>

        <ImageButton
            AbsoluteLayout.LayoutFlags="PositionProportional"
            AbsoluteLayout.LayoutBounds="1,1"
            WidthRequest="44"
            HeightRequest="44"
            CornerRadius="22"
            Margin="10"
            Padding="5">

            <ImageButton.Shadow>
                <Shadow />
            </ImageButton.Shadow>

            <ImageButton.Source>
                <FontImageSource />
            </ImageButton.Source>

        </ImageButton>
    </AbsoluteLayout>

</StackLayout>
</ContentPage>

The AbsoluteLayout is only there so I can float a button over top of the Editor. It may help with the layout issues, but I haven't tested without it, so I don't know. We used a Grid before switching to a StackLayout and left the AbsoluteLayout structure unchanged.

With this structure, and on MAUI 8 RC2, I've had zero issues with the Editor:

  • Editor correctly auto-scrolls when entering newlines past its bottom boundary
  • Editor correctly scrolls to cursor on TextChanged when cursor is outside its viewport
  • Editor correctly scrolls to cursor position when cursor position is set outside its viewport
  • Scrolling behaves as expected compared to other scrolling views like CollectionView

Thanks @tschbc for recommending the absolutelayout. I had a similar overlay editor layout and I was previously using a grid and that was causing some silly issues that seemed to autocorrect after the switch. You're the man!

@PureWeen PureWeen added the s/verified Verified / Reproducible Issue ready for Engineering Triage label Dec 11, 2023
@tj-devel709
Copy link
Contributor

Can confirm the behavior below on .NET 7 and .NET 8. Also appears to be completely separate from AutoKeyboardScrollManager scroll logic (for myself and others).

.NET 7 .NET 8
EditorIssueNet7.mov
EditorScrollIssueNet8.mov

@PureWeen
Copy link
Member

PureWeen commented Feb 9, 2024

Can you test with the latest nightly build?
https://github.com/dotnet/maui/wiki/Nightly-Builds

@last-Programmer
Copy link

@PureWeen i treid the nightly build and my project is not compiling. now it seems to be that platformview in handlers are object type and not the native type. and color.toPltform() is also broken. so cant test it.

@jaosnz-rep
Copy link
Collaborator

Verified this issue with Visual Studio Enterprise 17.10 Preview 1.0, can repro on iOS platform with sample project. DemoProj.zip

@jaosnz-rep jaosnz-rep added the s/triaged Issue has been reviewed label Feb 28, 2024
@bradencohen
Copy link
Contributor

bradencohen commented Mar 28, 2024

TLDR: Workaround

Go ahead and drop this handler in (only target your iOS platform), and it should hopefully make things more bearable:

/// <summary>
/// A handler for the <see cref="Editor"/> control.
/// </summary>
internal class EditorHandler : Microsoft.Maui.Handlers.EditorHandler
{
    /// <summary>
    /// Gets the desired size of the Editor.
    /// </summary>
    /// <param name="widthConstraint"></param>
    /// <param name="heightConstraint"></param>
    /// <returns></returns>
    public override Size GetDesiredSize( double widthConstraint, double heightConstraint )
    {
        //
        // Port of the default GetDesiredSizeFromHandler method (since internal):
        // https://github.com/dotnet/maui/blob/c7d1a4ec8d857aba674362d2777d98855f0ca67a/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs#L66
        //

        if ( PlatformView == null || VirtualView == null )
        {
            return new Size( widthConstraint, heightConstraint );
        }

        // The measurements ran in SizeThatFits percolate down to child views
        // So if MaximumWidth/Height are not taken into account for constraints, the children may have wrong dimensions
        widthConstraint = Math.Min( widthConstraint, VirtualView.MaximumWidth );
        heightConstraint = Math.Min( heightConstraint, VirtualView.MaximumHeight );

        CGSize sizeThatFits = PlatformView.SizeThatFits( new CGSize( ( float ) widthConstraint, ( float ) heightConstraint ) );

        var size = new Size(
            sizeThatFits.Width == float.PositiveInfinity ? double.PositiveInfinity : sizeThatFits.Width,
            sizeThatFits.Height == float.PositiveInfinity ? double.PositiveInfinity : sizeThatFits.Height );

        if ( double.IsInfinity( size.Width ) || double.IsInfinity( size.Height ) )
        {
            PlatformView.SizeToFit();
            size = new Size( PlatformView.Frame.Width, PlatformView.Frame.Height );
        }

        var finalWidth = ResolveConstraints( size.Width, VirtualView.Width, VirtualView.MinimumWidth, VirtualView.MaximumWidth );
        var finalHeight = ResolveConstraints( size.Height, VirtualView.Height, VirtualView.MinimumHeight, VirtualView.MaximumHeight );

        return new Size( finalWidth, finalHeight );
    }

    /// <summary>
    /// Port of the internal ResolveConstraints method.
    /// </summary>
    /// <param name="measured"></param>
    /// <param name="exact"></param>
    /// <param name="min"></param>
    /// <param name="max"></param>
    /// <returns></returns>
    internal static double ResolveConstraints( double measured, double exact, double min, double max )
    {
        var resolved = measured;

        min = Microsoft.Maui.Primitives.Dimension.ResolveMinimum( min );

        if ( Microsoft.Maui.Primitives.Dimension.IsExplicitSet( exact ) )
        {
            // If an exact value has been specified, try to use that
            resolved = exact;
        }

        if ( resolved > max )
        {
            // Apply the max value constraint (if any)
            // If the exact value is in conflict with the max value, the max value should win
            resolved = max;
        }

        if ( resolved < min )
        {
            // Apply the min value constraint (if any)
            // If the exact or max value is in conflict with the min value, the min value should win
            resolved = min;
        }

        return resolved;
    }
}

Research

image

This is the sus code (from the iOS EditorHandler). The problem only occurs for us when the height constraint is coming in as infinity, which also plays into the earlier messages about how VerticalStackLayout was not playing nicely.

Oddly enough, contrary to what the comment says, when I comment out the sus code no exceptions are thrown and everything seems to play nicely:

    public override Size GetDesiredSize( double widthConstraint, double heightConstraint )
    {
        //if ( double.IsInfinity( widthConstraint ) || double.IsInfinity( heightConstraint ) )
        //{
        //    // If we drop an infinite value into base.GetDesiredSize for the Editor, we'll
        //    // get an exception; it doesn't know what do to with it. So instead we'll size
        //    // it to fit its current contents and use those values to replace infinite constraints

        //    PlatformView.SizeToFit();

        //    if ( double.IsInfinity( widthConstraint ) )
        //    {
        //        widthConstraint = PlatformView.Frame.Size.Width;
        //    }

        //    if ( double.IsInfinity( heightConstraint ) )
        //    {
        //        heightConstraint = PlatformView.Frame.Size.Height;
        //    }
        //}

        var desiredSize = base.GetDesiredSize( widthConstraint, heightConstraint );
        Debug.WriteLine( "constraints:" + widthConstraint + "x" + heightConstraint );
        Debug.WriteLine( "size: " + desiredSize.Width + "x" + desiredSize.Height );

        return desiredSize;
    }

The ouput (that I slimmed down) seems to properly respect everything:

[0:] constraints:432x∞
[0:] size: 43.666666666666664x169

[0:] constraints:432x∞
[0:] size: 43.666666666666664x188

[0:] constraints:432x∞
[0:] size: 43.666666666666664x200

  <Editor
          MaximumHeightRequest="200"
          MinimumHeightRequest="100"
          BackgroundColor="Red"
          AutoSize="TextChanges" />

Before (code not commented out):
Xamarin Simulator Windows_1qE42tbzuG

After (code commented out):
Xamarin Simulator Windows_EcZKvKs4Nm

@Eilon Eilon removed the legacy-area-controls Label, Button, CheckBox, Slider, Stepper, Switch, Picker, Entry, Editor label May 10, 2024
@PureWeen PureWeen added p/2 Work that is important, but is currently not scheduled for release and removed p/1 Work that is important, and has been scheduled for release in this or an upcoming sprint labels Jul 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-controls-editor Editor p/2 Work that is important, but is currently not scheduled for release partner/cat 😻 this is an issue that impacts one of our partners or a customer our advisory team is engaged with platform/iOS 🍎 s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working
Projects
None yet
Development

No branches or pull requests