- Home /
How to keep the scroll bar handle at a fixed size?
Hi!
I'm trying to do a scrolling list, but when designing how it would look, i want the handle to stay at a fixed size no matter the size of the list (so I can make a custom sprite for it). Basically like a slider than a scrollbar, but I can't pass in a slider to the scroll rect component.
Is there a way I can restrict the size on the scrollbar's handle? Or a way I can use a slider to move the list instead of a scrollbar? Or maybe make the handle sprite a child of the handle object but have it actually reach the ends of the bar when it moves?
Answer by asafsitner · Nov 14, 2021 at 12:51 PM
It is possible to use a Slider for scrolling a ScrollRect by using ScrollRect.verticalNormalizedPosition. You can assign the value of the slider to the verticalNormalizedPosition (if you need to normalize the Slider's value just divide the current value with the max).
Also, since for a ScrollRect a verticalNormalizedPosition = 0 is the bottom, you may want to flip the orientation (or value) of the slider.
Example script:
using UnityEngine;
using UnityEngine.UI;
public class ScrollWithSliderTest : MonoBehaviour
{
public ScrollRect Rect;
public Slider ScrollSlider;
private void Update()
{
Rect.verticalNormalizedPosition = ScrollSlider.value;
}
}
Thank you! I'll try this out!
Will this only affect the scroll rect when using the slider? How about if I scroll by other means (like using touch/gamepad navigation inside the scroll rect), will it also update the slider's position? If not, is there a way I can do that without them canceling each other out? Is there something I can check to know if the scroll rect moves/changes?
Unfortunately, this will indeed prevent scrolling by any other means, as the Update function enforces the ScrollRect to the Slider's value. :(
To fix that, we'll want to use the Slider.onValueChanged event, like so:
using UnityEngine;
using UnityEngine.UI;
public class ScrollWithSliderTest: MonoBehaviour
{
public ScrollRect Rect;
public Slider ScrollSlider;
private void OnEnable()
{
ScrollSlider.onValueChanged.AddListener(UpdateScrollPosition);
}
private void OnDisable()
{
// important! Don't forget to unsubscribe from events!
ScrollSlider.onValueChanged.RemoveListener(UpdateScrollPosition);
}
private void UpdateScrollPosition(float value)
{
// Here I flip the value in the code instead of trying to rotate the UI element itself since it's easier for me :P
Rect.verticalNormalizedPosition = 1 - value;
}
}
That will allow us to maintain other methods with the ScrollRect, but now the problem of updating the Slider's value without causing an infinite loop remains. Unfortunately, Unity does not offer any solution to this out of the box, so a little more research into a workaround is needed.
EDIT:
It seems I was stuck in the past. Unity have since added the Slider.SetValueWithoutNotify API, which makes it very easy! Like before, we want to use the ScrollRect.onValueChanged event to update the Slider's value, but now we will call`SetValueWithoutNotify` to avoid the infinite loop:
using UnityEngine;
using UnityEngine.UI;
public class ScrollWithSliderTest: MonoBehaviour
{
public ScrollRect Rect;
public Slider ScrollSlider;
private void OnEnable()
{
ScrollSlider.onValueChanged.AddListener(UpdateScrollPosition);
Rect.onValueChanged.AddListener(UpdateSliderValue);
}
private void OnDisable()
{
// important! Don't forget to unsubscribe from events!
ScrollSlider.onValueChanged.RemoveListener(UpdateScrollPosition);
Rect.onValueChanged.RemoveListener(UpdateSliderValue);
}
private void UpdateScrollPosition(float value)
{
// Here I flip the value in the code instead of trying to rotate the UI element itself since it's easier for me :P
Rect.verticalNormalizedPosition = 1 - value;
}
private void UpdateSliderValue(Vector2 scrollPosition)
{
// Again, flippin the value for visual consistency
ScrollSlider.SetValueWithoutNotify(1 - scrollPosition.y);
}
}
It works perfectly! Thank you so much! You're the best!