maui icon indicating copy to clipboard operation
maui copied to clipboard

[iOS] Keyboard covers the entry

Open HobDev opened this issue 3 years ago • 14 comments

Description

The keyboard covers the entry in iOS. Whereas Entry should be above the Keyboard BugSample.zip .

Steps to Reproduce

Run the repro project. Try to enter something in the Entry.

Version with bug

Preview 13 (current)

Last version that worked well

Unknown/Other

Affected platforms

iOS

Affected platform versions

iOS 15

Did you find any workaround?

No.

Relevant log output

No response

HobDev avatar Feb 19 '22 06:02 HobDev

Same problem here in iOS on real divice ( Iphone 6s Plus) Microsoft Visual Studio Community 2022 (64-bit) - Preview Version 17.2.0 Preview 1.0

borisoprit avatar Feb 20 '22 19:02 borisoprit

verified on IOS: image

image

it works properly on Android: image

VincentBu avatar Mar 02 '22 09:03 VincentBu

I was expecting this issue to be solved by G.A

HobDev avatar May 24 '22 04:05 HobDev

I came here from https://github.com/dotnet/maui/issues/5121. I opened the sample from there and it still happens on 6.0.301 15.5 simulator.

svaldetero avatar Jul 29 '22 22:07 svaldetero

I think this is a very fatal problem, which makes it difficult for me to input text

xwang-888 avatar Aug 04 '22 05:08 xwang-888

It is not possible to publish app to the store until this bug is resolved.

HobDev avatar Aug 04 '22 11:08 HobDev

This problem is very serious. I don't know which version can be repaired in the future, I hope the development team can pay attention to it.

xwang-888 avatar Aug 04 '22 11:08 xwang-888

Still happening as of August 23, 2022

Vpatel541 avatar Aug 23 '22 18:08 Vpatel541

Still happening as of August 23, 2022

I think this problem is very serious, which makes my app unavailable to users. I don't know when this basic problem will be solved

xwang-888 avatar Aug 24 '22 01:08 xwang-888

@jsuarezruiz This issue needs to be resolved asap

Vpatel541 avatar Aug 26 '22 12:08 Vpatel541

@jsuarezruiz此问题需要尽快解决

It seems that no one cares about this problem.

xwang-888 avatar Aug 29 '22 03:08 xwang-888

@geraldmrv

Vpatel541 avatar Aug 29 '22 13:08 Vpatel541

I created a feature request with some example code that could solve this.

#10662

borrmann avatar Oct 12 '22 15:10 borrmann

Blocking for our application as well

legrignotin avatar Oct 14 '22 09:10 legrignotin

I created a feature request with some example code that could solve this.

https://github.com/dotnet/maui/issues/10662

I can't get to this link, the link is broken?

khambley avatar Oct 19 '22 18:10 khambley

@khambley

I can't get to this link, the link is broken?

The link was broken but the url is correct. https://github.com/dotnet/maui/issues/10662

svaldetero avatar Oct 19 '22 19:10 svaldetero

Any updates till today ? Any fixes ?

samirgcofficial avatar Oct 20 '22 09:10 samirgcofficial

They even put it into the backlog of work and thought it was not important,

xwang-888 avatar Oct 20 '22 09:10 xwang-888

I believe I have a clean workaround for the ScrollView control based on this Stackoverflow post: https://stackoverflow.com/a/32583809/15480238

The only issue is I don't fully understand when/where DisconnectHandler should be called as it doesn't get called by the platform, so the UIKeyboard notifications will stack up as new instances are created.

public class KeyboardAvoidingScrollViewHandler : Microsoft.Maui.Handlers.ScrollViewHandler
{
    NSObject keyboardShowObserver;
    NSObject keyboardHideObserver;

    protected override void ConnectHandler(UIScrollView platformView)
    {
        base.ConnectHandler(platformView);

        keyboardShowObserver = UIKeyboard.Notifications.ObserveWillShow(OnKeyboardShow);
        keyboardHideObserver = UIKeyboard.Notifications.ObserveWillHide(OnKeyboardHide);
    }

    private void OnKeyboardShow(object sender, UIKeyboardEventArgs args)
    {
        var scrollView = PlatformView;
        if (scrollView == null)
        {
            return;
        }

        var keyboardFrame = UIKeyboard.FrameEndFromNotification(args.Notification);

        scrollView.ContentInset = new UIEdgeInsets(
            scrollView.ContentInset.Top,
            scrollView.ContentInset.Left,
            keyboardFrame.Size.Height,
            scrollView.ContentInset.Right
        );
    }

    private void OnKeyboardHide(object sender, UIKeyboardEventArgs e)
    {
        var scrollView = PlatformView;
        if (scrollView == null)
        {
            return;
        }

        scrollView.ContentInset = UIEdgeInsets.Zero;
    }

    protected override void DisconnectHandler(UIScrollView platformView)
    {
        if (keyboardShowObserver != null)
        {
            keyboardShowObserver.Dispose();
            keyboardShowObserver = null;
        }

        if (keyboardHideObserver != null)
        {
            keyboardHideObserver.Dispose();
            keyboardHideObserver = null;
        }

        base.DisconnectHandler(platformView);
    }
}

And in the app startup:

....
.ConfigureMauiHandlers(handlers =>
{
#if IOS
    handlers.AddHandler<ScrollView, KeyboardAvoidingScrollViewHandler>();
#endif
})

rschroeder avatar Oct 23 '22 23:10 rschroeder

@rschroeder I'm new to MAUI. Where did you put your KeyboardAvoidingScrollViewHandler? When I copied that code into my project, .NET MAUI can't seem to find things like NSObject and UIKeyboardEventArgs. I assume I need a using statement but I can't figure out where these types come from in .NET MAUI.

Ranger-Rick avatar Nov 04 '22 18:11 Ranger-Rick

@Ranger-Rick since it is platform specific code you should put the renderer into the Platform/iOS folder. The usings should be: using UIKit; and using Foundation;

borrmann avatar Nov 04 '22 18:11 borrmann

Neither of those using statements are found in my project... Do I need to configure something in my project to get those namespaces?

Ranger-Rick avatar Nov 04 '22 18:11 Ranger-Rick

Oh, I figured it out. If you are not currently targeting iOS or Mac in your build options, IDEs will not pickup that you are trying to use an Apple namespace. Switching my target fixed the problem. I also had to include an #if IOS line to encapsulate the using statements

Ranger-Rick avatar Nov 04 '22 19:11 Ranger-Rick

This is still an issue... Considering a keyboard takes up more than 1/3 of the screen, not having the ScrollView (or a Grid or any other scaling containers for that matter) adjusting when a keyboard pops up is severely limiting UI design options without having to use hacky workarounds...

Edit: Just adding the hacky workaround I went with for now https://stackoverflow.com/questions/72536074/keyboard-covering-form-elements-on-ios-on-net-maui-app

Zyvox avatar Nov 07 '22 12:11 Zyvox

If you are making chat type application then this work around is going to look horrible… 😊’. @Zyvox

Samirgc avatar Nov 07 '22 13:11 Samirgc

For anybody interested, this is what is going in my MAUI project to handle this bug. It was influenced by @rschroeder 's answer but takes an alternative approach. The below code is an EntryHandler. This way, whenever the keyboard appears for an Entry (in any context, not just when the entry is inside a scrollview), the app adjusts to ensure the entry is visible. This is achieved by updating the Application.Current.MainPage.TranslationY property. This feels a little hacky and gross, but these be the times we're in. If anyone has any suggestions to make this better please let me know 😄

#if IOS
using Microsoft.Maui.Platform;
using UIKit;
using Foundation;


namespace MyNameSpace;

/// <summary>
/// An EntryHandler that overrides the MAUI Platform Entry Handler to better handle Entry focus events.
/// When this Handler is registered with the MAUI app, all Entries will automatically scroll the page to ensure the
/// focused Entry rests above the keyboard
/// </summary>
public class EntryHandler : Microsoft.Maui.Handlers.EntryHandler
{
    private NSObject _keyboardShowObserver;
    private NSObject _keyboardHideObserver;
    
    
    protected override void ConnectHandler(MauiTextField platformView)
    {
        base.ConnectHandler(platformView);

        //iOS has dedicated keyboard notifications that fire whenever the keyboard will show and will hide.
        // Here, we are registering events to fire when the keyboard is shown or hidden
        _keyboardShowObserver = UIKeyboard.Notifications.ObserveWillShow(OnKeyboardShow);
        _keyboardHideObserver = UIKeyboard.Notifications.ObserveWillHide(OnKeyboardHide);
    }

    private void OnKeyboardShow(object sender, UIKeyboardEventArgs args)
    {
        //If the MainPage is null for some reason, do not continue
        if (Application.Current?.MainPage is null) return;
        
        //If the entry that called this method is not the currently focused entry, do not scroll the page
        if (!PlatformView.Focused) return;

        //Some devices have a pixel density for some reason. We want to convert the screen height into pixels by
        // dividing the MainDisplay Height by the pixel density (to get actual pixels)
        var density = DeviceDisplay.Current.MainDisplayInfo.Density;
        var mainDisplay = DeviceDisplay.Current.MainDisplayInfo.Height / density;
        
        //Grab the height of the keyboard. NOTE: This is the HEIGHT of the keyboard, which always appears from the bottom
        // At this time, it is safe to say that the height of the keyboard is also the ending Y pixel (when measured from
        // the bottom of the screen)
        var keyboardHeight = args.FrameEnd.Height;
        
        //Grab the Y position of the focused entry. Just to make your life more complicated, this value is calculated 
        // from the TOP. So we need to subtract the Y position of the entry from the amount of vertical pixels in the display
        // to get the ending Y coordinate for the Entry.
        var entryYPosition = mainDisplay - PlatformView.AccessibilityFrame.Y;
        
        //Grab the height of the focused Entry. This is important because we want to scroll the page to ensure the 
        // entire entry is visible. Without this value, the page would scroll to the top border of the entry (hiding the
        // main content under the keyboard).
        var entryHeight = PlatformView.Frame.Height;
        

        //If the keyboard height is greater than the Y position of the entry, the keyboard is about to cover the entry
        if (keyboardHeight >= entryYPosition)
        {
            //Determine how many pixels to scroll. Add some extra padding just for appearances
            var scrollAmount = keyboardHeight - entryYPosition + entryHeight + 10;
            Application.Current.MainPage.TranslationY = scrollAmount * -1;
        }
    }
    
    private void OnKeyboardHide(object sender, UIKeyboardEventArgs args)
    {
        if (Application.Current?.MainPage is null) return;
        
        //When the keyboard is dismissed, reset the scroll of the page
        Application.Current.MainPage.TranslationY = 0;
    }

    //There is a problem with .NET MAUI at the time of writing this control: DisconnectHandler is never called
    // This means that every entry in the entire app is always listening for keyboard events. This is less than ideal.
    // If performance begins to take a hit, consider an alternative approach.
    protected override void DisconnectHandler(MauiTextField platformView)
    {
        if (_keyboardShowObserver != null)
        {
            _keyboardShowObserver.Dispose();
            _keyboardShowObserver = null;
        }
    
        if (_keyboardHideObserver != null)
        {
            _keyboardHideObserver.Dispose();
            _keyboardHideObserver = null;
        }
    
        base.DisconnectHandler(platformView);
    }
}
#endif

Ranger-Rick avatar Nov 07 '22 19:11 Ranger-Rick

This bug is still present in .NET 7 Maui . Does anyone knows about the timeline by which this will be resolved.

HobDev avatar Nov 16 '22 06:11 HobDev

Also keeping me from releasing my app. This issue is open for more than 9 months now... @jsuarezruiz @PureWeen @Redth @jfversluis Any chance that anyone of you guys can have look on this one? Seems to be a game stopper for iOS at the moment.

AndreasReitberger avatar Nov 16 '22 14:11 AndreasReitberger

Confirmed in latest version of .NET MAUI as well, has not been resolved

Vpatel541 avatar Nov 16 '22 15:11 Vpatel541

I already had this problem in Xamarin and made a GridRenderer for a Grid control that stayed on top of the keyboard. So all entries moved with the keyboard up. Due to another bug i cant get that custom renderer to work in MAUI https://github.com/dotnet/maui/issues/9936

As a workaround i try/want to make a handler for my grid (something like the scrollview fix above) but i cant find a GridViewHandler .... What am i missing?

npostma avatar Nov 16 '22 15:11 npostma