Author Topic: Best way of forcing anchoring updates on non-OnUpdate anchored widgets?  (Read 2979 times)

bac9

  • Full Member
  • ***
  • Thank You
  • -Given: 2
  • -Receive: 4
  • Posts: 113
    • View Profile
Let's say I have a label which grows vertically to fit texts of different length - for example, item descriptions. Anchored to the label, there is a widget offset by, say, -24 on the left, 24 on the right, 24 on the top, -24 on the bottom. In turn, anchored to the top, left and right (never bottom) of that widget are various secondary child elements, like a clamped header with an item name, item preview images, fixed-size lines with item stats and so on. To illustrate, here is how this looks:

Game camera view:


Scene view of the primary label and sole widget directly parented to it:


Scene view of the secondary elements anchored to the boundary widget:


There is a small problem with that sort of setup. While it works like you see on the gifs in Edit mode, Play mode is a different story. Unless you make set every single anchor to OnUpdate refresh mode (which you definitely don't want to do if you want to avoid GC from anchoring operations generated every frame), there is no way to actually force a refresh on all those elements after you change the text on that label. I've tried a lot of methods exposed by NGUI UIPanel and looked at how on-anchor refreshing is done in UIRect/UIWidget/UIPanel and I think there is no standard way to do that for OnEnable/OnStart anchors.

Please correct me if I'm wrong and actually missed some method on UIPanel or other NGUI class meant to force refresh of all anchored widgets. I went on with an assumption that no such method exists and tried to force it manually, with my own methods. I focused on an attempt to devise a method that would force correctly ordered anchor refresh on every single widget under a certain panel - while forcing an anchoring update of an individual widget is easy, it's not a useful approach, since it would require every anchoring-influencing object to keep explicit references to all anchored dependent objects. I just wanted to call something like panel.ForceAnchorRefresh () from my label, which keeps the UI nicely decoupled. Here is what I got:

  1. public void ForceAnchorRefresh ()
  2. {
  3.     ForceAnchorRefreshRecursive (transform);
  4. }
  5.  
  6. private void ForceAnchorRefreshRecursive (Transform parent)
  7. {
  8.     UIRect rect = parent.GetComponent<UIRect> ();
  9.     if (rect != null)
  10.         rect.UpdateAnchors ();
  11.  
  12.     for (int i = 0; i < parent.childCount; ++i)
  13.         ForceAnchorRefreshRecursive (parent.GetChild (i));
  14. }

This works great, with sole exception of cases where widgets influencing other widgets but not being their parents aren't guaranteed to update first. But that's easy to prevent if you structure your UI with strict parenting reflecting anchoring dependencies. Is there anything wrong with this approach, or it's a sound way to force refresh of OnStart/OnEnable anchored widgets after their dependency was resized?

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
In your hierarchy, your label comes after the child elements. Did you try simply moving it up, so that it's above its sibling? The idea is to update the label's anchors first, not last -- since it's the most important component in your case.

Although thinking about it, the way updating anchors works is if there's an anchor dependency, it will be updated first... but this assumes OnUpdate type updates.

I remember having to do a forced update for EnvelopContent to ensure that all child anchors get updated. Line 55 of EnvelopContent:
  1. BroadcastMessage("UpdateAnchors", SendMessageOptions.DontRequireReceiver);
I think a similar broadcast would be easier to set up in your case than what you're trying to do.

bac9

  • Full Member
  • ***
  • Thank You
  • -Given: 2
  • -Receive: 4
  • Posts: 113
    • View Profile
Thanks, that helps! Enforcing parenting (dependent elements are always under referenced elements) and forcing anchor update from the panel in the right order does the trick, allowing me to never use OnUpdate anchors, pushing updates only on frames where I actually change UI content.