unity developer
unity developer

Reputation: 33

Is there way to only open the mobile keyboard when using inputfields in unity3d on the OnMouseUp() method?

I want to know if there is a way to only open the keyboard after the click OnMouseUp() not on the OnMouseDown() method. I have tried to modify the inputfield.cs script but it does not open.

here is the video link

Upvotes: 1

Views: 2026

Answers (1)

Programmer
Programmer

Reputation: 125275

It's possible but tricky and complicated especially when new to Unity.

Do the following:

1. From the Editor disable interactable on the InputFields you want to only open keyboard when key is released. On the "Disabled Color" of the InputField, make sure to change that to white and the alpha to full (255) so that the InputField will not look like it is disabled.

enter image description here


You don't have to do the remaining steps below because it's already done in the script at the end of this post but the steps below shows you how this is done.

2. When the InputField interactable is disabled, it cannot accept clicks anymore. Clicking on it will not open the keyboard.

You now have to use GraphicRaycaster.Raycast to do a manual UI raycast. The result will be stored in a List of RaycastResult.

3. Since you want to open the keyboard only when the click or touch is released, put the GraphicRaycaster.Raycast inside if (Input.GetKeyUp(KeyCode.Mouse0)) for desktop devices and if(Input.GetTouch(0).phase == TouchPhase.Ended) for mobile devices. They will both return true on the frame when click or touch is released.

4. When the click is released with the example in #3, check if InputField is in the List of the RaycastResult with result.GetComponentInParent<InputField>(), if it is an InputField, manually select the InputField with the SetSelectedGameObject and OnPointerClick functions. After this, the keyboard should open on the InputField and that's when the key is released not when pressed.

After this, register to the onEndEdit event on the InputField.

5. When the onEndEdit event is called, de-select the InputField with the DeactivateInputField function, un-register from the onEndEdit event with the RemoveAllListeners function then disable or set interactable back to false.


Do step #1 only for all the InputField you want to only open keyboard when click or touch is released then, create a script named "ReleaseInputOpenKeyboard", copy the script below into it then attach it to an empty GameObject in the scene. The mobile-keyboards will only show on the InputField when the click or touch is released instead of when it is pressed.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class ReleaseInputOpenKeyboard: MonoBehaviour
{
    GraphicRaycaster m_Raycaster;
    EventSystem m_EventSystem;

    List<GameObject> inputFieldProcessedThisFrame = new List<GameObject>();

    void Start()
    {
        m_Raycaster = GameObject.Find("Canvas").GetComponent<GraphicRaycaster>();
        //Fetch the Event System from the Scene
        m_EventSystem = FindObjectOfType<EventSystem>();
    }


    void Update()
    {
#if UNITY_STANDALONE || UNITY_EDITOR
        //DESKTOP COMPUTERS
        if (Input.GetKeyUp(KeyCode.Mouse0))
        {
            SelectInputField();
        }
#else
        if ((Input.touchCount > 0) && (Input.GetTouch(0).phase == TouchPhase.Ended))
        {
            SelectInputField();
        }
#endif
    }

    void SelectInputField()
    {
        //Set up the new Pointer Event
        PointerEventData m_PointerEventData = new PointerEventData(m_EventSystem);
        //Set the Pointer Event Position to that of the mouse position
        m_PointerEventData.position = Input.mousePosition;

        //Create a list of Raycast Results
        List<RaycastResult> results = new List<RaycastResult>();

        //Raycast using the Graphics Raycaster and mouse click position
        m_Raycaster.Raycast(m_PointerEventData, results);

        //For every result returned, output the name of the GameObject on the Canvas hit by the Ray
        foreach (RaycastResult result in results)
        {
            InputField ipf = result.gameObject.GetComponentInParent<InputField>();
            //Check if object hit is an InputField and make sure we have not processed this object this frame
            if (ipf && !inputFieldProcessedThisFrame.Contains(ipf.gameObject))
            {
                Debug.Log("Hit " + ipf.gameObject.name);

                //Mark this InputField as processed
                inputFieldProcessedThisFrame.Add(ipf.gameObject);

                //Enable interactable
                ipf.interactable = true;

                //Focus on the InputField
                ActivateInputField(ipf, m_EventSystem);

                //Add event to detect when the Input is deselected
                ipf.onEndEdit.AddListener(delegate { OnInputEnd(ipf); });
            }
        }

        //Reset processed InputField
        if (inputFieldProcessedThisFrame.Count > 0)
            inputFieldProcessedThisFrame.Clear();
    }

    void ActivateInputField(InputField ipf, EventSystem evSys)
    {
        evSys.SetSelectedGameObject(ipf.gameObject, new BaseEventData(evSys));
        ipf.OnPointerClick(new PointerEventData(evSys));
    }


    //Detects when InputField is deselected then disable interactible
    void OnInputEnd(InputField ipf)
    {
        StartCoroutine(OnInputEndCOR(ipf));
    }

    IEnumerator OnInputEndCOR(InputField ipf)
    {
        //Disable interactable then remove event
        ipf.DeactivateInputField();
        ipf.onEndEdit.RemoveAllListeners();

        /*Wait until EventSystem is no longer in selecting mode
         This prevents the "Attempting to select while already selecting an object" error
         */
        while (EventSystem.current.alreadySelecting)
            yield return null;

        ipf.interactable = false;
    }
}

Upvotes: 2

Related Questions