dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.25k stars 1.76k forks source link

[regression/8.0.3] Keyboard regression bugs #19214

Closed williambuchanan2 closed 2 months ago

williambuchanan2 commented 11 months ago

Description

Exactly as documented here:

https://github.com/dotnet/maui/issues/14173#issue-1638336366

This issue was resolved in June last year and is now back in .NET 8.

Here are the iOS issues I see with the keyboard:

  1. Once the keyboard appears, you can't access the lower part of the UI. This means you can't access any fields on the bottom half of the screen while the keyboard is visible.
  2. If you tap directly into one of those lower fields before the keyboard is showing, then the keyboard comes up over the top of the field, so you can't see what you are typing.
  3. When the keyboard shows, in Android the screen scrolls up from the bottom to make way for the keyboard. However in iOS the whole screen moves upwards, so you also lose the top of the screen. This is happening to me when I have a grid as the main container on the page and I have an editor stuck to the bottom of the page - I end up not being able to see the editor, and I also lose the top of the screen. So to summarise - you lose access to both the top and bottom of the screen.

All the above is different to the behaviour on Android, and native iOS apps. If you can just get it to behave exactly like Android does then there won't be any problems.

Steps to Reproduce

Open reproduction app. Tap the "Keyboard Problem" button. Tap one of the top fields - soft keyboard shows. Now try to scroll down to get to one of the bottom fields on the form Notice bottom of form is not accessible when soft keyboard is showing.

Link to public reproduction project repository

https://github.com/williambuchanan2/MauiNavigation

Version with bug

8.0.3

Is this a regression from previous behavior?

Yes, this used to work in .NET MAUI

Last version that worked well

7.0.101

Affected platforms

iOS

Affected platform versions

No response

Did you find any workaround?

No

Relevant log output

No response

tj-devel709 commented 8 months ago

The #2 issue from the first post here should be fixed and #3 is being discussed for the future. This long thread with different issues makes it harder to be sure the issues are getting full attention / we have proper reproduction so I'll close this thread after issue #1 is fixed, and we can start new issues with any other ios keyboard scrolling related things. This way, we can ensure the issues are getting proper attention, we can add them to the proper project boards, and we can try to get quicker fixes out to all of you. Thank you!

williambuchanan2 commented 7 months ago

Unfortunately it seems that #3 is the critical part of the problem, and without a fix for that, from what I can see, the other fixes don't seem to have solved much. The end result is that we still lose access to half the page when the keyboard is showing.

RobTF commented 7 months ago

Didn't this all work in Xamarin? Plus doesn't the native keyboard on the various platforms handle this stuff by default (i.e. moving the content up so it is accessible)? It seems strange that MAUI is somehow breaking this, what's the deal?

davibittencourtome commented 7 months ago

Didn't this all work in Xamarin? Plus doesn't the native keyboard on the various platforms handle this stuff by default (i.e. moving the content up so it is accessible)? It seems strange that MAUI is somehow breaking this, what's the deal?

To solve problem #1 in xamarin I had to create a CustomPageRenderer to get around the problem of pushing the list up. I tried using the same one on Maui, but was unsuccessful.

RobTF commented 7 months ago

Good to know Xamarin wasn't perfect. Anecdotally, I just don't recall having end users complain so much about the keyboard. A good chunk of our bugs were related to keyboards overlapping screens and users getting "trapped" as buttons weren't tappable due to being scrolled offscreen. We've had to enable that "tap to close keyboard" attribute on almost every page to give users an escape hatch, but the UI looks naff when someone taps on an entry and has to key stuff in blind.

With Xamarin we didn't give it a second thought - we threw it all on the page and it just seemed to work like any other native app.

williambuchanan2 commented 7 months ago

Didn't this all work in Xamarin? Plus doesn't the native keyboard on the various platforms handle this stuff by default (i.e. moving the content up so it is accessible)? It seems strange that MAUI is somehow breaking this, what's the deal?

I believe this has been an unresolved problem from the Xamarin years. Hard to believe it's been broken all those years. You would think the default behaviour on the platform would kick in but doesn't seem to be the case. The thing is it works perfectly on Android - all they have to do is make it work exactly the same way as Android but for some reason that doesn't seem to be happening.

tschbc commented 7 months ago

I recently spent too much time coming up with a workaround to fix keyboard overlapping in my app.

In my case, we show a PageSheet modal with a large multiline Editor with a toolbar above it. I created a Behavior<View> that resizes the height of the main ContentView in the page when the keyboard is opened.

More specifically, that Behavior<View> decreases its bound View's height by the intersection Y of the keyboard and the View. While I attached this behavior to the base ContentView we place in the modal page, it should work for any layout that you want to resize. Hopefully. I've only tested my specific scenario.

Project.Behaviors.SoftKbResizeBehavior.cs:

namespace Project.Behaviors;

public partial class SoftKbResizeBehavior : Behavior<View>
{
    View View { get; set; }

    protected override void OnAttachedTo(View bindable)
    {
        base.OnAttachedTo(bindable);

        View = bindable;
        View.Loaded += View_Loaded;
        View.Unloaded += View_Unloaded;
    }

    private void View_Loaded(object sender, EventArgs e)
    {
        Attach();
    }

    private void View_Unloaded(object sender, EventArgs e)
    {
        View.Loaded -= View_Loaded;
        View.Unloaded -= View_Unloaded;

        Detach();
    }

    partial void Attach();

    partial void Detach();
}

Project.Platforms.iOS.Behaviors.SoftKbResizeBehavior.cs:

// Adapted from https://developer.apple.com/documentation/uikit/uiresponder/1621578-keyboardframeenduserinfokey

using CoreGraphics;
using Foundation;
using Microsoft.Maui.Handlers;
using UIKit;

namespace Project.Behaviors;

public partial class SoftKbResizeBehavior
{
    UIView UIView { get; set; }

    NSObject ObserveWillChangeFrameToken { get; set; }

    partial void Attach()
    {
        UIView = (View.Handler as ViewHandler).PlatformView;

        ObserveWillChangeFrameToken = UIKeyboard.Notifications.ObserveWillChangeFrame(OnKeyboardWillChangeFrame);
    }

    partial void Detach()
    {
        ObserveWillChangeFrameToken.Dispose();
    }

    void OnKeyboardWillChangeFrame(object sender, UIKeyboardEventArgs e)
    {
        var intersection = CGRect.Intersect(UIView.Frame, e.FrameEnd);

        if (intersection.IsEmpty)
        {
            View.HeightRequest = -1;
            View.VerticalOptions = LayoutOptions.Fill;
        }
        else
        {
            var uiViewFrameInWindowCoordinateSpace = UIView.CoordinateSpace.ConvertRectToCoordinateSpace(
                UIView.Frame, UIView.Window.CoordinateSpace);

            var viewMaxY = uiViewFrameInWindowCoordinateSpace.GetMaxY();
            var intMinY = intersection.GetMinY();

            var offset = viewMaxY - intMinY;

            View.HeightRequest = View.Height - offset;
            View.VerticalOptions = LayoutOptions.Start;
        }
    }
}

Example of how I use it:

<?xml version="1.0" encoding="utf-8" ?>
<ContentView>

    <ContentView.Behaviors>
        <behaviors:SoftKbResizeBehavior />
    </ContentView.Behaviors>

    <Border>

        <StackLayout Orientation="Vertical">

            <Grid>

                <!-- Toolbar items here -->

            </Grid>

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

                <!-- Views to float over the Editor -->

            </AbsoluteLayout>

        </StackLayout>
    </Border>

</ContentView>

Notes:

davibittencourtome commented 6 months ago

This problem also occurs in Xamarin.Forms, I solved it by adapting the following code XF issue and it still works today. For xamarin it is a PageRenderer, but for Maui I believe it should be a CustomPageController and CustomPageHandler, but I couldn't make it work. If anyone can manage it, it would be a great temporary fix for the problem.

RuddyOne commented 5 months ago

Any update on this? Seems to still be broken from what I can see.

TheXenocide commented 5 months ago

I recently spent too much time coming up with a workaround to fix keyboard overlapping in my app.

Thanks @tschbc that workaround was a huge help. I applied this behavior to all ScrollViews in my app using a Style to set the behavior. I still had to apply some manual scrolling adjustments as only the top line or two of my Editor was scrolling into view. In case anyone else is having similar issues, I adapted some stuff we had from XF which essentially consisted of:

It's not perfect but seems to work well enough for now.

ETA: Oh, I forgot, I also had to change some ScrollViews that had VerticalStackLayouts as their child original child so that it was nested as the first row in a Grid with RowDefinitions="Auto,*" with a BoxView in the bottom row to force it to take up vertical space if the content of the scroll view didn't take up the whole page.

ETA (Again 😅): I should probably mention that it appears the reason our editor may have required a little extra elbow grease is because it expands its height when it is focused (so it's the same height as an Entry until its focused). We tried a variety of approaches to get the size thing to work as close to our earlier XF version as best we could but none of them worked perfectly out-of-box. We went with using the common Visual States to adjust the height on focus in the end.

ramonB1996 commented 2 months ago

@TheXenocide Could you show us some code of your workaround? Would be very helpful, as I am struggling with the Editor control myself at the moment.