Apparently UI Documents aren’t initialized on the first frame.
I have this example code that prints the bounds of a Label to the console.
public class UiTest : MonoBehaviour
{
[SerializeField]
private UIDocument _uiDocument;
private Label _label;
private int frameCount;
private void Awake()
{
_label = _uiDocument.rootVisualElement.Q<Label>("MyLabel");
//prints (x:0.00, y:0.00, width:NaN, height:NaN)
PrintWorldBounds("Awake");
}
private void Start()
{
//prints (x:0.00, y:0.00, width:NaN, height:NaN)
PrintWorldBounds("Start");
}
private void Update()
{
//prints (x:0.00, y:0.00, width:NaN, height:NaN) on the first frame, from the second frame on, it prints the right value
PrintWorldBounds($"Update {frameCount.ToString()}");
frameCount++;
}
private void PrintWorldBounds(string prefix)
{
Debug.Log($"{prefix}: {_label.worldBound.ToString()}");
}
}
The comments explain the issue. On the first frame, the UI Document doesn’t seem to be initialized. Is this by design or a bug? It makes interacting with a UI Document by code tricky because you have to delay everything.
If it’s by design, is there a way to know if the document is initialized other than waiting one frame?
I didn’t test this, but OnEnable happens before the first Update and in the first Update the values are still not initialized.
EDIT: Sorry, you meant that the query happened too early, not the printing of the world bounds. After testing, it seems that it doesn’t make a difference at which point the query happens. Even if the query is made in the first update, uninitialized bound values are still printed.
Interestingly there’s another strange initialization thing going on. Label.uitkTextHandle is only initialized after Alt-Tabbing away from Unity and back again. While this is not an officially accessible property (I access it via reflection), it’s still a weird thing to happen and I wonder why.
For consistency you would presumably need to update the values when the layout changes. Subscribing to the label’s GeometryChangedEvent would be the way to get a callback whenever the layout is recalculated, and would be the appropriate place for this code imo