Search Unity

  1. Unity 2018.3 is now released.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. We've updated our Terms of Service. Please read our blog post from Unity CTO and Co-Founder Joachim Ante here
    Dismiss Notice
  4. Want to provide direct feedback to the Unity team? Join the Unity Advisory Panel.
    Dismiss Notice
  5. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice

non-interactable UI element (e.g. button) not skipped by navigation

Discussion in 'Unity UI & TextMesh Pro' started by garv3, Dec 13, 2014.

  1. garv3


    Dec 30, 2012
    If I set a UI element (e.g. a button) as not interactable, I would expect it to be skipped by the navigation events (up, down, left, right), but it can still be selected. It won't change the Transition graphic, but it is not skipped and even its OnSelect is triggered.
    Is this intended or a bug?
  2. hoesterey


    Mar 19, 2010
    Seams intended. I could see wanting the user see info on a "Locked' button. E.g. A level that is not selectable yet. Though an option would be nice.
  3. garv3


    Dec 30, 2012
    Then maybe there should at least be an animation option (e.g. color tint, image...) to set for a selected non-interactive element. Right now the user can not see, if the element is selected.
    If I have three elements for example:
    left: interactableElement - center: NONinteractableElement - right: NONinteractableElement
    then there is a problem. If the left element is selected by default and the user navigates to the right, he can only see that the element is not selected any more, but he is unable to see which element is currently selected. If he now presses the "right arrow key" again, nothing changes visually but actually the right element is selected. What he would at least expect now is, that if he presses the "left arrow key", the left element should be selected again. But still nothing happens. Confusion is the result. This can not be the way it is meant to be...
  4. TechCor


    Apr 3, 2015
    This problem is really this old? Blah.

    There is no way around this problem if you can't use automatic mode. It should really pass through like automatic does.

    It's almost like no one uses controllers on menu screens with non-interactable buttons with Unity.

    Hey, I'm offline. Let me just gray out this "social" button between these other menu options.
    Wait, why is my highlight disappearing? Oh, it selected this button that the user obviously doesn't want to go to.

    I could maybe see being able to select it to bring up some kind of help popup, but one could just as easily fake the gray out by replacing the sprite. On the other hand, you practically have to replace the navigation system to get around this problem. I can't tell the non-interactable button to just pass through to the next, so I'd either have to write my own nav system on top of Unity's or have conditionals for every non-interactable button that handles each direction that I might want to pass through.

    This isn't even the only problem with the nav system. I've already had to write a special helper script to take care of a bunch of other poor design problems.
  5. karl_jones


    Unity Technologies

    May 5, 2015
  6. TechCor


    Apr 3, 2015
    Yes, I saw that feedback before posting and gave it a couple votes, but I'm surprised that no one else has mentioned it. Maybe they just work around it. Speaking of which, here is a workaround for anyone else having this issue. I still think this should be default behavior though (consistent with automatic mode).

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.UI;
    4. using UnityEngine.EventSystems;
    6. public class SkipNonInteractable : MonoBehaviour, ISelectHandler
    7. {
    8.    private Selectable m_Selectable;
    10.    // Use this for initialization
    11.    void Awake()
    12.    {
    13.      m_Selectable = GetComponent<Selectable>();
    14.    }
    16.    public void OnSelect(BaseEventData evData)
    17.    {
    18.      // Don't apply skipping unless we are not interactable.
    19.      if (m_Selectable.interactable) return;
    21.      // Check if the user navigated to this selectable.
    22.      if (Input.GetAxis("Horizontal") < 0)
    23.      {
    24.        Selectable select = m_Selectable.FindSelectableOnLeft();
    25.        if (select == null || !select.gameObject.activeInHierarchy)
    26.          select = m_Selectable.FindSelectableOnRight();
    27.        StartCoroutine(DelaySelect(select));
    28.      }
    29.      else if (Input.GetAxis("Horizontal") > 0)
    30.      {
    31.        Selectable select = m_Selectable.FindSelectableOnRight();
    32.        if (select == null || !select.gameObject.activeInHierarchy)
    33.          select = m_Selectable.FindSelectableOnLeft();
    34.        StartCoroutine(DelaySelect(select));
    35.      }
    36.      else if (Input.GetAxis("Vertical") < 0)
    37.      {
    38.        Selectable select = m_Selectable.FindSelectableOnDown();
    39.        if (select == null || !select.gameObject.activeInHierarchy)
    40.          select = m_Selectable.FindSelectableOnUp();
    41.        StartCoroutine(DelaySelect(select));
    42.      }
    43.      else if (Input.GetAxis("Vertical") > 0)
    44.      {
    45.        Selectable select = m_Selectable.FindSelectableOnUp();
    46.        if (select == null || !select.gameObject.activeInHierarchy)
    47.          select = m_Selectable.FindSelectableOnDown();
    48.        StartCoroutine(DelaySelect(select));
    49.      }
    50.    }
    52.    // Delay the select until the end of the frame.
    53.    // If we do not, the current object will be selected instead.
    54.    private IEnumerator DelaySelect(Selectable select)
    55.    {
    56.      yield return new WaitForEndOfFrame();
    58.      if (select != null || !select.gameObject.activeInHierarchy)
    59.        select.Select();
    60.      else
    61.        Debug.LogWarning("Please make sure your explicit navigation is configured correctly.");
    62.    }
    63. }
    Add this script to any buttons that need to be skipped when not interactable.

    Note: This does not stop event propagation. Any other scripts using OnSelect() need to check for interactable.

    Code now handles noninteractable buttons that can't pass through (sends it back the other direction). Includes handling inactive objects.
    Last edited: Feb 23, 2016
    tomtominc, Sigma266 and ben-maurin like this.
  7. LightStriker


    Aug 3, 2013
    That bug is so God damn dumb. How is that even a thing? (Almost 2 years later)
  8. YVanhoutte


    Mar 5, 2018
    Good to see I'm not the only one stumbling on this.
    I worked around it myself since I have dynamic content for my UI (in which case thanks to indentation the automatic navigation was giving very inconsistent results), but to me this once again highlights the importance of having a separation of highlighting and selecting objects in the eventsystem.
  9. Max_Bol


    May 12, 2014
    The main issue I seem to see in this topic is how some interpret the "Interactable" option which mean the issues is from the user, not the system.

    The "Interactable" is just an option that allow you to set quickly if the button returns its OnClick() function upon being activated as well as setting the related transition image or color automatically. You could call it "Lock the button" and it wouldn't be wrong. If you want something like having the button skipped all-together because it's not interactable, then you got to script it yourself.

    You might find it absurd or might seem like a bug to you, but that's simply the logical behavior which is also the easiest (simplest) to work with if you want custom behaviors in your UI.

    Here are examples of where the "Interactable" can be used as is:

    If you have an input menu followed by a button that bring the player to the next phase. You want the player to only be able to move on to the next phase if the text typed in the input menu fits within a set of rules. (The input Content Type is limited and you might want to add some additional rules like making it impossible to type specific words.) If the player type too few letters or have something that goes against the "rules", the button "Next" or "Register" (or whatever) is grayed out as it's "Interectable" is turned off and only turn on if the rules are followed.

    If you have a menu where the player gain some kind of advancement (like skill points), but each skills have requirement. When the requirement is not met, the "Interactable" of the skill's button is turned off. You might want the player to be able to have some kind of pop-up that mention what's missing to get this skill when selected even if the "Interactable" is turned off. (That's why the "OnSelect()" is activated when an non-interactable button is selected.)

    If you have a server list, you might want the player to be able to select the servers that are full even if they are unable to join those.

    If you want to use an Explicit navigation where non-interactable buttons are skipped, you got to do update the navigation through scripts which is relatively easy to do in a few additional lines of codes. It all depends on how you set up your menus' buttons. The golden rule, when it comes in making and scripting your UI is to keep track of everything in your menu through something like lists and arrays. Make it quite easy to find back through references afterwards.

    Is it a pain in the neck? Yeah sure! But that's the best and most optimal way of allowing everyone to handle the buttons' interaction the way each wants.
  10. YVanhoutte


    Mar 5, 2018
    Well, another way they could handle this is instead of rolling navigation and selectable behaviours into one component is actually split that into two components with a dependancy (selectable and all its derived classes rely on navigation or a derived class) and let us override JUST the navigation.
    Right now if you want different navigation rules (for example categorise your labels in a dropdown tree and restrict navigation on one axis following these categories) you EITHER have to write a custom selectable and inherit it for EVERY kind of selectable there is (buttons, toggles, input fields, ... ) or do as I did and write a separate component that overrides the navigation of the standard selectable...
    Neither solution is elegant nor idiot-proof.
    Standard automatic navigation is fine for (dynamic) strict grids, but any dynamic content that needs to be navigatable in a non-grid structure is a nightmare.
  11. daville


    Aug 5, 2012
    I just found this issue also.

    Perhaps an exta state "Non Interactable Selected" could solve the problem or a Checker to "Pass" the selection to the next one on the list.