Dropdown, how to show items around the selected

Hi,
I have a question regarding the Dropdown component (Redirecting to latest version of com.unity.ugui)
From the docs : “Once clicked, it opens up the list of options so a new option can be chosen.”

I have a lot of options (let’s say 100) and I would like to open the list of options around the current item in the dropdown avoiding the tedious manual scrolling for the user.

For example: the current item is the 50th, if I click the Dropdown it should show items around that (48-52)

Thank you

Ok, I solved my own problem with a ugly piece of code:

    public void OnPointerClickDelegate(string data)
    {
        GameObject ddl = GameObject.Find("Dropdown List");
        GameObject vp = ddl.GetComponentsInChildren<RectTransform>()[1].gameObject;
        GameObject content = vp.GetComponentsInChildren<RectTransform>()[1].gameObject;
   
        RectTransform rt = content.GetComponent<RectTransform>();

        int index = Dropdown.value;
        // 75 is the height of an item in my dropdown
        rt.position = rt.position + Vector3.up * index * 75;
    }

As you can see I reach the transform of the options content and set its position to a value proportional to the current index in the dropdown.

Please note:
The code uses Find, which I don’t like (because it works with fragile strings)
The code relies on GetComponentsInChildren items position (which is not guaranteed)
You can optimize the hierarchy accessors, I wanted to explicit my purposes here

You need to trigger this function adding an Event Trigger on the Dropdown component from the inspector.

4 Likes

THANKS ALOT, WORKS PERFECTLY

Another solution:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
[RequireComponent(typeof(ScrollRect))]
public class ScrollRectAutoScroll : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
    public float scrollSpeed = 10f;
    private bool mouseOver = false;

    private List<Selectable> m_Selectables = new List<Selectable>();
    private ScrollRect m_ScrollRect;

    private Vector2 m_NextScrollPosition = Vector2.up;
    void OnEnable()
    {
        if (m_ScrollRect)
        {
            m_ScrollRect.content.GetComponentsInChildren(m_Selectables);
        }
    }
    void Awake()
    {
        m_ScrollRect = GetComponent<ScrollRect>();
    }
    void Start()
    {
        if (m_ScrollRect)
        {
            m_ScrollRect.content.GetComponentsInChildren(m_Selectables);
        }
        ScrollToSelected(true);
    }
    void Update()
    {
        // Scroll via input.
        InputScroll();
        if (!mouseOver)
        {
            // Lerp scrolling code.
            m_ScrollRect.normalizedPosition = Vector2.Lerp(m_ScrollRect.normalizedPosition, m_NextScrollPosition, scrollSpeed * Time.deltaTime);
        }
        else
        {
            m_NextScrollPosition = m_ScrollRect.normalizedPosition;
        }
    }
    void InputScroll()
    {
        if (m_Selectables.Count > 0)
        {
            if (Input.GetButtonDown("Horizontal") || Input.GetButtonDown("Vertical") || Input.GetButton("Horizontal") || Input.GetButton("Vertical"))
            {
                ScrollToSelected(false);
            }
        }
    }
    void ScrollToSelected(bool quickScroll)
    {
        int selectedIndex = -1;
        Selectable selectedElement = EventSystem.current.currentSelectedGameObject ? EventSystem.current.currentSelectedGameObject.GetComponent<Selectable>() : null;

        if (selectedElement)
        {
            selectedIndex = m_Selectables.IndexOf(selectedElement);
        }
        if (selectedIndex > -1)
        {
            if (quickScroll)
            {
                m_ScrollRect.normalizedPosition = new Vector2(0, 1 - (selectedIndex / ((float)m_Selectables.Count - 1)));
                m_NextScrollPosition = m_ScrollRect.normalizedPosition;
            }
            else
            {
                m_NextScrollPosition = new Vector2(0, 1 - (selectedIndex / ((float)m_Selectables.Count - 1)));
            }
        }
    }
    public void OnPointerEnter(PointerEventData eventData)
    {
        mouseOver = true;
    }
    public void OnPointerExit(PointerEventData eventData)
    {
        mouseOver = false;
        ScrollToSelected(false);
    }
}

Use: Places this component in the Template of Dropdown (with the ScrollRect component)

Link to original: Unity3d ScrollRect Auto-Scroll, Dropdown Use: Places this component in the Template of Dropdown (with the ScrollRect component) · GitHub

I created this simple script to solve the problem:

//The TMP Dropdown object in the unity hierarchy has a "Template" object as a child, in that object is a "Viewport" and a "Scrollbar".
//Place this script on that "Scrollbar" object in the "Template". 
//This will get called once on Start due to the script on the template, that is why there is a null check below,
//  otherwise this gets created every time the Dropdown menu is activated, it reads what value the dropdown is at and creates a ratio to 
//  the total number of objects. Then it sets the scrollbar value to that ratio less than one. This puts the selected option in the viewport
//  everytime the Dropdown is activated.
//
//                                                                          ---JCC913z---

using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class DropdownHandler : MonoBehaviour
{
    Scrollbar _scrollbar;
    TMP_Dropdown _dropdown;

    private void Start()
    {
        _dropdown = GetComponentInParent<TMP_Dropdown>();
        _scrollbar = GetComponent<Scrollbar>();

        if (_scrollbar != null) { GoToSelected(); }
    }

    private void GoToSelected()
    {
        float selectedOption = _dropdown.value;
        float ratio = selectedOption / _dropdown.options.Count;

        _scrollbar.value = 1f - ratio;
    }
}

Hope this helps someone!

Great solution, thank you so much!