UI Document layout isn't initialized on the first frame

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?

Use OnEnable to query your root visual element. Awake is too early.

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.

The layout hasn’t run until PreLateUpdate, so until that point the layout of elements is undefined.

Is this by design? If yes, I think it’s a pretty bad design decision.

Yes, just as adding an element doesn’t mean it’s performed layout.
What reason you have for reading the layout immediately though?

I stumbled upon this issue when tinkering around with this problem I have: Interacting with subsections of text. - #29 by Nit_Ram

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. :smiley:

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

How can this event be accessed? I can’t find any member like that on a label.

Use the RegisterCallback function on the label:

1 Like

Ah nice, thank you very much!