shouldHideSoftKeyboard bug on iOS?

Hello, we’re encountering an issue in our project where it’s having a hard time correctly working with an iPad’s Smart Keyboard.

If we never set shouldHideSoftKeyboard to anything, the TMPRO inputs can detect and use the keyboard input correctly. Or, if we only set it to false, it works correctly.

But if we start detecting whether the keyboard is attached, or not, and switch it back and forth from true to false, it stops detecting keyboard input.

After updating to the 3.2.0 preview version, it now accepts one character input, and then stops. So it seems like they fixed it, but only for 1 frame or 1 input.

Maybe it has something to do with this “fix” we saw in this new version:

Or maybe we’re just not using it quite right. We can’t find any documentation for shouldHideSoftKeyboard.
Very strange. If you have any tips about how we might be able to fix this, that would be superb.

Thanks!

    private void UpdateControlType()
    {
        if (MobileCheck())
        {
            if (GameManagers.currentControl == GameManagers.CurrentControlType.Physical_Keyboard)
            {
                shouldHideSoftKeyboard = false;
            }
            else
            {
                shouldHideSoftKeyboard = true;
            }
        }
    }

Well, the “solution” was curious. It turns out that calling this function from Update broke it for Smart Keyboards. Not exactly sure why.

Also, simply referencing the status of shouldHideSoftKeyboard n a debug statement, from within a LateUpdate function, broke it. Fragile.

Looks like the core issue is tied to this bug:

You can occasionally find ways to keep it from this behavior, but it’s very sensitive.

Ah, and it seems like the Pre-Release 3.2 TMPro release is definitely helping. That’s why it’s working as well as it is now. But it’s still pretty fragile. I’ll see if we can put together a clean file we can use to submit for a bug report.

I am facing a very similar bug on the Quest 2. Digging into the TMP_InputField code, it really feels like this line of code in LateUpdate on line 1537 is the culprit.

            if (InPlaceEditing() && isKeyboardUsingEvents() || !isFocused)
            {
                return;
            }

On Android platforms:
InPlaceEditing() will return true if TouchScreenKeyboard.isSupported (always true on Android) and either shouldHideSoftKeyboard or shouldHideMobileInput is set to true.

        private bool InPlaceEditing()
        {
            if (Application.platform == RuntimePlatform.WSAPlayerX86 || Application.platform == RuntimePlatform.WSAPlayerX64 || Application.platform == RuntimePlatform.WSAPlayerARM)
                return !TouchScreenKeyboard.isSupported || m_TouchKeyboardAllowsInPlaceEditing;

            if (TouchScreenKeyboard.isSupported && shouldHideSoftKeyboard)
                return true;

            if (TouchScreenKeyboard.isSupported && shouldHideSoftKeyboard == false && shouldHideMobileInput == false)
                return false;

            return true;
        }

That’s all good. If you want to handle the input yourself, set one of those two properties to true, but…

isKeyboardUsingEvents() will never return true on Android due to this:

        private bool isKeyboardUsingEvents()
        {
            switch (Application.platform)
            {
                case RuntimePlatform.Android:
                case RuntimePlatform.IPhonePlayer:
                case RuntimePlatform.tvOS:
                #if UNITY_2020_2_OR_NEWER
                case RuntimePlatform.PS4:
                    #if !(UNITY_2020_2_1 || UNITY_2020_2_2)
                    case RuntimePlatform.PS5:
                    #endif
                #endif
                case RuntimePlatform.Switch:
                    return false;
                default:
                    return true;
            }
        }

What this means is that “InPlaceEditing() && isKeyboardUsingEvents()” will never be true on Android, meaning that the above if statement will only ever be true if the input field is not focused.

I think the if statement should be changed to:

            if (InPlaceEditing() || isKeyboardUsingEvents() || !isFocused)
            {
                return;
            }

If someone has more info on this and has a good reason why it should be &&, I’d love to be enlightened, but my understanding of this bit of code is telling me it should be ||.

As a bonus observation, wouldn’t the isKeyboardUsingEvents() method mean that you couldn’t attach a USB/Bluetooth keyboard to an Android device and use that? I’ve never tried using a physical keyboard on an Android device before, so I’m not sure.

@Tricnic , we haven’t dug into the Android part yet, so I don’t have anything helpful to say about your insight yet, although I suspect you’re right.

However, I need to add that it seems this issue has been “fixed.” Part of the problem may have been our very large project code. Part of it may be that now we’re on the Pre-Release 3.2 AND we updated from 2021.3.21f1 to 2021.3.22f1.

But in a nutshell, we finally got the functionality to basically work like we wanted it to with Smart Keyboards on iPads. Keyboard input was being recognized. But we were still seeing some weird stuff and errors. And then in the endless pursuit of tracking down all these weird bugs, we tested out just totally disabling this effort to manually switch back and forth between triggering shouldHideSoftKeyboard and shouldHideMobileInput . . . and that fixed it.

I’m not sure if TMPro updated something or 2021.3.22f1 fixed something. But the “fixes” we were using previously were working, but also causing other problems and breaking it. So now it SEEMS to work just fine, automatically, switching smoothly back and forth between iPads with or without a Smart Keyboard. Without any “help” from us.

We’re really not sure what happened, but if you’re having issues try just not messing with these settings at all and see if it might just work perfectly for you now.

BTW, this is broken on us again now. Argh. My current theory is that this “fixed” bug got broken again:

See the thread that pointed to this:

Wondering if this plugin might help, even though we use TMPro inputs:

Update: Solved it (again) by updating (again). Apparently they broke this around 2021.3.25f1, but fixed it again by 2021.3.31f1.

You can fix this bug by making the TMP package local and changing the code of the TMP_InputField class on line 1553 from this one:

Standart code

            if (m_SoftKeyboard == null || m_SoftKeyboard.status != TouchScreenKeyboard.Status.Visible)
            {
                if (m_SoftKeyboard != null)
                {
                    if (!m_ReadOnly)
                        text = m_SoftKeyboard.text;

                    if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.LostFocus)
                        SendTouchScreenKeyboardStatusChanged();

                    if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.Canceled)
                    {
                        m_ReleaseSelection = true;
                        m_WasCanceled = true;
                        SendTouchScreenKeyboardStatusChanged();
                    }

                    if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.Done)
                    {
                        m_ReleaseSelection = true;
                        OnSubmit(null);
                        SendTouchScreenKeyboardStatusChanged();
                    }
                }

                OnDeselect(null);
                return;
            }

To this:
New code

            if (m_SoftKeyboard == null)
            {
                return;
            }
         
            if (m_SoftKeyboard.status != TouchScreenKeyboard.Status.Visible)
            {
                if (!m_ReadOnly)
                    text = m_SoftKeyboard.text;

                if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.LostFocus)
                    SendTouchScreenKeyboardStatusChanged();

                if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.Canceled)
                {
                    m_ReleaseSelection = true;
                    m_WasCanceled = true;
                    SendTouchScreenKeyboardStatusChanged();
                }

                if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.Done)
                {
                    m_ReleaseSelection = true;
                    OnSubmit(null);
                    SendTouchScreenKeyboardStatusChanged();
                }

                OnDeselect(null);
                return;
            }

Unity version 2021.3.0f1
TMP version: 3.0.7

This worked for me. I hope it will help you!

A couple of versions later (Unity 2022.3.21f1 and TMP 3.0.9), I have a similar problem.
It happens in VR on Quest 2 (Android), with a custom keyboard. The flags “hide soft keyboard” and “hide mobile input” should be true.

OnDeselect is always called immediately after OnSelect and the caret indicator disappears, the input field can’t be focused at all.

I fixed it by inheriting the TMP_InputField.cs and overriding the method OnDeselect with an empty body. This caused some other problems, for example, a wrong position of the caret indicator and a focused field after “submit”, so I had to move the caret position and call DeactivateInputField().
This “patched” the problem in my case, but I hope a real fix will be released.

public class TMP_InputField_Custom : TMP_InputField {
    public override void OnDeselect(BaseEventData eventData) {
        //base.OnDeselect(eventData); // We don't call the base method because OnDeselect is mistakenly executed at the wrong moment
    }
    private void Start() {
        onValueChanged.AddListener((s) => {
            caretPosition = text.Length; // Manually set the caret position to the end of the text because it is not updated automatically
        });
    }
    private void OnDestroy() {
        onValueChanged.RemoveAllListeners();
    }
}