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>());
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:
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
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: