ScrollView.ScrollTo() doesn't seem to work in runtime

I’m trying to implement a scrollview in runtime mode:

VisualElement outputElement = new VisualElement();
// do stuff to the output element
//...

// add the element to the scrollview
outputList.Add(outputElement);

// scroll to the bottom
outputList.ScrollTo(outputList.Children().Last<VisualElement>());

also tried

outputList.ScrollTo(outputElement);

The scrollview just doesn’t scroll.

Can you try to delay the execution of “ScrollTo” to the next frame?

Basically, currently this method only works after the layout has finished executing. We should expose a version of ScrollTo() which allows forcing the layout (with the potential performance hit) immediately.

But in the mean time, you could post-pone it like this:

outputList.RegisterCallback<GeometryChangedEvent>(FirstLayoutCallback);

private void FirstLayoutCallback(GeometryChangedEvent evt)
{
     outputList.UnregisterCallback(FirstLayoutCallback);
     outputList.ScrollTo(outputList.Children().Last<VisualElement>());
}

Adding a immediate option would be awesome!

Adding the registered callback directly to the list won’t work, as the scroller isn’t updated when the scrollview GeometryChangedEvent is raised. Instead you have to register on the scroll view’s contentContainer GeometryChangedEvent. See https://discussions.unity.com/t/792393/8

@antoine-unity
ScrollView has still no immediate version of ScrollTo(). Is there any news? (UI Toolit 1.0.0-pre.17)

No news, I’ll revive this discussion in the team.

Are the solutions provided above not working for you?

Hi. I achieved it by calling ScrollTo() in the next frame. But the code looks not clear and hard to understand, so I’m waiting for immediate version.

Hey there,

just wanted to update here since I have been stuck in the same issue for a day. This solution is still the only I found that work when you want to update a ScrollView scroll position when it’s opening. Usefull when you have a list where players can see the details and then they want to go back to the same position.

I am trying to use ScrollTo inside the Editor and I have ran into the same issue. I had to use the editor update callback in order to execute the ScrollTo in the next frame. Would be great to have an immediate version.

Also, even with such a delayed execution scheme, it still sometimes fails for me.

I use this method to scroll to something in a ScrollView.
Works nicely

public static void ScrollToImmediate(this ScrollView scrollView, VisualElement item) {

    if (item == null || scrollView == null) {
        return;
    }

    int remainingIterations = 4;

    void TryScroll() {

        //if both layout and item have a size, then we can scroll immediate
        //otherwise, we need to listen to layout changes then scrollTo

        if (item.layout.width > 0 && scrollView.layout.width > 0) {

            scrollView.ScrollTo(item);
            return;
        }
        else if (remainingIterations <= 0) {

            Debug.LogWarning("Too many layout iterations");

            scrollView.ScrollTo(item);
            return;
        }

        if (scrollView.layout.width > 0) {

            item.RegisterCallback<GeometryChangedEvent, VisualElement>(OnGeometryChanged, item);
        }
        else {

            scrollView.RegisterCallback<GeometryChangedEvent, VisualElement>(OnGeometryChanged, scrollView);
        }
    }

    void OnGeometryChanged(GeometryChangedEvent evt, VisualElement target) {

        target.UnregisterCallback<GeometryChangedEvent, VisualElement>(OnGeometryChanged);

        //try scrolling after we received a geometry changed event from either the item or scrollView
        //the geometry still might be 0, so keep trying if so

        remainingIterations--;

        TryScroll();
    }

    TryScroll();
}

If there’s a better way to determine if something has had a layout pass, I’d love to know.
But checking width > 0 works

Similar trouble here with Unity 2023.2.18. n my case, i want to scroll to when the panel is not attached yet. It looks like the first geometry update is not the correct one (e.g. 5000px height). It’s the complete height, without scrollbars. The second geometry change is than the correct one (e.g. 300px height), where we can call ScrollTo:

panel.RegisterCallbackOnce<GeometryChangedEvent>(evt => {
    panel.RegisterCallbackOnce<GeometryChangedEvent>(evt => {
        panel.ScrollTo(selected);
     });
});

This realy feels bad, can’t we get a support that the ScrollView do this by itself when its not attached yet? :slight_smile: