How do I close a UI element when a tap occurs outside of it?

2.5k views Asked by At

There are a lot of solutions to this problem floating around, but none of them seem to work properly for me. I have a button that opens a list of selections in a UI element, and I want it to close when clicked outside of it. I currently have this:

private void OnEnable()
{
    EventSystem.current.SetSelectedGameObject(gameObject);
}

public void OnDeselect(BaseEventData eventData)
{
    //Close the Window on Deselect only if a click occurred outside this panel
    if (!mouseIsOver)
        gameObject.SetActive(false);
}

public void OnPointerEnter(PointerEventData eventData)
{
    mouseIsOver = true;
    EventSystem.current.SetSelectedGameObject(gameObject);
}

public void OnPointerExit(PointerEventData eventData)
{
    mouseIsOver = false;
    EventSystem.current.SetSelectedGameObject(gameObject);
}

Which works fine on a PC, but unfortunately due to there not being an actual pointer on mobile, it closes the panel even if clicked inside. I have tried using something like this:

foreach (Touch touch in Input.touches)
    {
        int id = touch.fingerId;
        if (EventSystem.current.IsPointerOverGameObject(id))
        {
            isClicked = true;
        }
        if (Input.GetMouseButtonDown(0))
        {
            // Check if the mouse was clicked over a UI element
            if (EventSystem.current.IsPointerOverGameObject())
            {
                isClicked = true;
            }
        }
    }

But that has not worked either. This seems like an incredibly simple problem and I don't understand why I can't find a simple solution to it.

1

There are 1 answers

0
Nikolay Gonza On

I recomend using Unity technic when dropdown is created.

Look at this link

The main idea is to create blocker behind your button and the other ui elements.

Your open panel funcion should look something like this:

private RectTransform gameObjectTransform;
public void OpenPanel()
{
    CreateBlocker();
    gameObjectTransform = transform.parent;
    transform.SetParent(GetComponentInParent<Canvas>());
    transform.SetAsLastSibling();
}

Create blocker method like in docs:

private GameObject currentBlocker;
public void CreateBlocker()
{
    GameObject gameObject = new GameObject("Blocker");
    RectTransform rectTransform = gameObject.AddComponent<RectTransform>();
    rectTransform.SetParent(ChatGraphics.Instance.mainCanvas, false);
    rectTransform.anchorMin = (Vector2)Vector3.zero;
    rectTransform.anchorMax = (Vector2)Vector3.one;
    rectTransform.sizeDelta = Vector2.zero;
    Canvas canvas = gameObject.AddComponent<Canvas>();
    canvas.overrideSorting = true;
    canvas.sortingLayerID = component.sortingLayerID;
    canvas.sortingOrder = component.sortingOrder - 1;
    gameObject.AddComponent<GraphicRaycaster>();
    gameObject.AddComponent<Image>().color = Color.clear;
    gameObject.AddComponent<Button>().onClick.AddListener(delegate { Hide(); });
    currentBlocker = gameObject;
}

And last is Hide method which should be called every time (even if blocker does not triggered)

public void Hide()
{
    if (currentBlocker != null)
        Destroy(currentBlocker);
    if (gameObjectTransform != null)
        transform.SetParent(gameObjectTransform);
    // Your code to hide your panel
}