- Home /
Unity dropdown doesn't scroll when navigating with arrow keys
The unity dropdown that I've made in my scene doesn't scroll to the selected object when there are more options than the screen can handle.
It happens when I try to navigate with the arrow keys through the dropdown list.
Answer by GibTreaty · Jun 19, 2016 at 11:54 AM
Here's a script I just made for dropdown menus in my project. You won't be able to use it without some modifications as I've coded it to work with some Rewired inputs that are specific to my project. I also put the script on the "Template" child game object of the Dropdown menu object. It's not perfect and it only supports vertical auto scrolling but it gets the job done in my case.
using System.Collections.Generic;
using Rewired;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class ScrollRectAutoScroll : MonoBehaviour {
List<Selectable> selectables = new List<Selectable>();
int elementCount;
#region Properties
Player RewiredPlayer { get; set; }
ScrollRect ScrollRectComponent { get; set; }
#endregion
void OnEnable() {
if(ScrollRectComponent) {
ScrollRectComponent.content.GetComponentsInChildren(selectables);
elementCount = selectables.Count;
}
}
void Awake() {
RewiredPlayer = ReInput.players.GetPlayer(0);
ScrollRectComponent = GetComponent<ScrollRect>();
}
void Start() {
ScrollRectComponent.content.GetComponentsInChildren(selectables);
elementCount = selectables.Count;
}
void Update() {
if(elementCount > 0)
if(RewiredPlayer.GetButtonDown("Menu Horizontal") || RewiredPlayer.GetNegativeButtonDown("Menu Horizontal") || RewiredPlayer.GetButtonDown("Menu Vertical") || RewiredPlayer.GetNegativeButtonDown("Menu Vertical")) {
int selectedIndex = -1;
Selectable selectedElement = EventSystem.current.currentSelectedGameObject ? EventSystem.current.currentSelectedGameObject.GetComponent<Selectable>() : null;
if(selectedElement)
selectedIndex = selectables.IndexOf(selectedElement);
if(selectedIndex > -1)
ScrollRectComponent.verticalNormalizedPosition = 1 - (selectedIndex / ((float)elementCount - 1));
}
}
}
Ayyyyy, thanks for replying! I totally forgot about this post.
I found a solution online some time ago, maybe you can optimize yours, if it is even necessary combining the two?
using UnityEngine;
//using DG.Tweening;
using UnityEngine.UI;
[RequireComponent(typeof(ScrollRect))]
public class ScrollRectEnsureVisible : $$anonymous$$onoBehaviour
{
#region Public Variables
public float _AnimTime = 0.15f;
public bool _Snap = false;
public RectTransform _$$anonymous$$askTransform;
#endregion
#region Private Variables
private ScrollRect mScrollRect;
private RectTransform mScrollTransform;
private RectTransform mContent;
#endregion
#region Unity $$anonymous$$ethods
private void Awake ()
{
mScrollRect = GetComponent<ScrollRect> ();
mScrollTransform = mScrollRect.transform as RectTransform;
mContent = mScrollRect.content;
}
#endregion
#region Public $$anonymous$$ethods
public void CenterOnItem(RectTransform target)
{
// Item is here
var itemCenterPositionInScroll = GetWorldPointInWidget(mScrollTransform, GetWidgetWorldPoint(target));
Debug.Log("Item Anchor Pos In Scroll: " + itemCenterPositionInScroll);
// But must be here
var targetPositionInScroll = GetWorldPointInWidget(mScrollTransform, GetWidgetWorldPoint(_$$anonymous$$askTransform));
Debug.Log("Target Anchor Pos In Scroll: " + targetPositionInScroll);
// So it has to move this distance
var difference = targetPositionInScroll - itemCenterPositionInScroll;
difference.z = 0f;
//clear axis data that is not enabled in the scrollrect
if (!mScrollRect.horizontal)
{
difference.x = 0f;
}
if (!mScrollRect.vertical)
{
difference.y = 0f;
}
//this is the wanted new position for the content
var newAnchoredPosition = mContent.anchoredPosition3D + difference;
if (_Snap) {
mContent.anchoredPosition3D = newAnchoredPosition;
} else {
//DOTween.To (() => mContent.anchoredPosition, x => mContent.anchoredPosition = x, newAnchoredPosition, _AnimTime);
}
}
#endregion
#region Private $$anonymous$$ethods
Vector3 GetWidgetWorldPoint(RectTransform target)
{
//pivot position + item size has to be included
var pivotOffset = new Vector3(
(0.5f - target.pivot.x) * target.rect.size.x,
(0.5f - target.pivot.y) * target.rect.size.y,
0f);
var localPosition = target.localPosition + pivotOffset;
return target.parent.TransformPoint(localPosition);
}
Vector3 GetWorldPointInWidget(RectTransform target, Vector3 worldPoint)
{
return target.InverseTransformPoint(worldPoint);
}
#endregion
}
Answer by kylewill713 · Jul 21, 2016 at 04:29 PM
This script did the trick for me
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
[RequireComponent(typeof(ScrollRect))]
public class ScrollRectEnsureVisible : MonoBehaviour
{
RectTransform scrollRectTransform;
RectTransform contentPanel;
RectTransform selectedRectTransform;
GameObject lastSelected;
void Start()
{
scrollRectTransform = GetComponent<RectTransform>();
}
void Update()
{
//just incase content panel gets created in start.
if(contentPanel == null) contentPanel = GetComponent<ScrollRect>().content;
GameObject selected = EventSystem.current.currentSelectedGameObject;
if (selected == null)
{
return;
}
if (selected.transform.parent != contentPanel.transform)
{
return;
}
if (selected == lastSelected)
{
return;
}
selectedRectTransform = selected.GetComponent<RectTransform>();
contentPanel.anchoredPosition = new Vector2(contentPanel.anchoredPosition.x, - (selectedRectTransform.localPosition.y) - (selectedRectTransform.rect.height/2));
lastSelected = selected;
}
}
The code above works well, but had some problems with ScrollRect content position which I fixed with simple $$anonymous$$athf.Clamp.
[Edit] Fixed weird behavior when using the dropdown menu with mouse with On$$anonymous$$ouseOver and On$$anonymous$$ouseExit events. Small downside for this being that the dropdown will not scroll with gamepad if the mouse is hovering the dropdown menu.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
[RequireComponent(typeof(ScrollRect))]
public class ScrollRectEnsureVisible : $$anonymous$$onoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
RectTransform scrollRectTransform;
RectTransform contentPanel;
RectTransform selectedRectTransform;
GameObject lastSelected;
Vector2 targetPos;
void Start()
{
scrollRectTransform = GetComponent<RectTransform>();
if (contentPanel == null)
contentPanel = GetComponent<ScrollRect>().content;
targetPos = contentPanel.anchoredPosition;
}
void Update()
{
if (!_mouseHover)
Autoscroll();
}
public void Autoscroll()
{
if (contentPanel == null)
contentPanel = GetComponent<ScrollRect>().content;
GameObject selected = EventSystem.current.currentSelectedGameObject;
if (selected == null)
{
return;
}
if (selected.transform.parent != contentPanel.transform)
{
return;
}
if (selected == lastSelected)
{
return;
}
selectedRectTransform = (RectTransform)selected.transform;
targetPos.x = contentPanel.anchoredPosition.x;
targetPos.y = -(selectedRectTransform.localPosition.y) - (selectedRectTransform.rect.height / 2);
targetPos.y = $$anonymous$$athf.Clamp(targetPos.y, 0, contentPanel.sizeDelta.y - scrollRectTransform.sizeDelta.y);
contentPanel.anchoredPosition = targetPos;
lastSelected = selected;
}
bool _mouseHover;
public void OnPointerEnter(PointerEventData eventData)
{
_mouseHover = true;
}
public void OnPointerExit(PointerEventData eventData)
{
_mouseHover = false;
}
}
Just logged in to say that I love you man. This worked perfectly fine for me. Thank you and have some sweet Christmas days!
Thank you @Vipsu! The script works perfectly. Though this should be added in Unity UI.
Your answer
![](https://koobas.hobune.stream/wayback/20220612074623im_/https://answers.unity.com/themes/thub/images/avi.jpg)