Search Unity

LayoutGroup does not refresh in its current frame.

Discussion in 'UGUI & TextMesh Pro' started by castor76, Feb 27, 2017.

  1. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    I have various issues with layoutgroup componens such as vertical layout group or grid layout group, where the expected refreshing and rebuilding of the layout due to the change of its child content is not happening at the current frame of which the change occur causing the UI to briefly "flash" with wrong look for a split second (probably just a frame) before it rebuilds and find the correct place.

    I have tried to use commands such as

    LayoutRebuilder.MarkLayoutForRebuild or LayoutRebuilder.ForceRebuildLayoutImmediate but nothing seems to actually work.

    I have double and triple checked to make sure the content change does happen within the same frame.

    It's frustrating to trying to hack this to make it work such as enabling the canvas after one frame etc..in which case the result of layout seems to be fine at the cost of one frame not actually showing the UI so it is just a different annoying issue.

    Any ideas?
     
  2. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    I've had similar issues with trying to get layouts to force update, but gave up and decided to just go with Canvas.ForceUpdateCanvases() instead.

    It was either that, or do a deep dive into the UI code to fix it there which was time I really didn't have.
     
  3. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    I have tried that too. But it was the same..just a frame later rebuild everytime.
     
  4. Quit

    Quit

    Joined:
    Mar 5, 2013
    Posts:
    63
    Can someone @Unity elaborate on this more?
     
  5. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    813
    A year later, also wondering about this. Getting the same one-frame flash.
     
  6. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    813
    Found the solution. It's very simple, but
    LayoutRebuilder.ForceRebuildLayoutImmediate
    is misleading. Its first parameter is called
    layoutRoot
    , but in fact, as another user discovered, you must call it on each LayoutGroup individually, not on a parent of layout groups.

    So I created this utility method instead and it refreshes, in the same frame:

    Code (CSharp):
    1. public static void RefreshLayoutGroupsImmediateAndRecursive(GameObject root)
    2. {
    3.     foreach (var layoutGroup in root.GetComponentsInChildren<LayoutGroup>())
    4.     {
    5.         LayoutRebuilder.ForceRebuildLayoutImmediate(layoutGroup.GetComponent<RectTransform>());
    6.     }
    7. }
    8.  
     
  7. spsarras

    spsarras

    Joined:
    Dec 13, 2019
    Posts:
    1
    I think you also need to make sure you are updating the root LayoutGroup last. I had a nested situation where I was disabling some LayoutGroups and this was not working the first time I was toggling the state! No idea why it subsequently worked, but I guess the order of the LayourGroups from GetComponentsInChildren was changing after the first state change and the parent was placed lower. I did something like that and it worked:


    public static void RefreshLayoutGroupsImmediateAndRecursive(GameObject root)
    {
    var componentsInChildren = root.GetComponentsInChildren<LayoutGroup>(true);
    foreach (var layoutGroup in componentsInChildren)
    {
    LayoutRebuilder.ForceRebuildLayoutImmediate(layoutGroup.GetComponent<RectTransform>());
    }

    var parent = root.GetComponent<LayoutGroup>();
    LayoutRebuilder.ForceRebuildLayoutImmediate(parent.GetComponent<RectTransform>());
    }
     
    JohnnyConnor, bab202 and chrpetry like this.
  8. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    Why.... after all these years, there is no simple solution that just works.

    I just want to call LayoutRebuilder.ForceRebuildLayoutImmediate(RectTransform)

    And have it remesh, relayout everything at the point of calling it, so it properly renders it at the same frame as I called it.

    Garhhhh, what's all this work arounds and case by case stuff.. come on now.. Unity.

    What's worse, is that there seems to be so many work around for so many different situations and in my case, no matter what I do, I can't make it to work other than just have to yield and wait for the next frame and then have the UI alpha set to 1.
     
    MaxLohMusic and Misha_Kh like this.
  9. marcin-huuuge

    marcin-huuuge

    Joined:
    Sep 5, 2018
    Posts:
    15
    I agree it's about time Unity stops pumping new features and tries to fix all of the broken essential things that are a pain to work with for years now.
     
    JohnnyConnor and Misha_Kh like this.
  10. Adam_is_learning_all_the_time

    Adam_is_learning_all_the_time

    Joined:
    Jun 29, 2016
    Posts:
    12
    This is my first time suggesting code to help anyone - I hope it helps!

    I noticed that my objects look okay if I disable, then re-enable a UI element in the layout group - This is a IENumerator that automates this process using WaitForEndOfFrame. I'd love to hear any alternatives, but this works okay my side in case it's useful. :)

    Code (CSharp):
    1.     public void AddTeam()
    2.     {
    3.         GameObject newTeam = Instantiate(teamButtonTemplate, teamsContainer);
    4.         newTeam.SetActive(true);
    5.         StartCoroutine(ReEnableAfterFrame(newTeam));
    6.     }
    7.  
    8. //Reusable, just put a gameobject that's part of the layoutgroup and needs resetting visually
    9.     IEnumerator ReEnableAfterFrame(GameObject theObject)
    10.     {
    11.         theObject.SetActive(false);
    12.         yield return new WaitForEndOfFrame();
    13.         theObject.SetActive(true);
    14.     }
     
    AdamBebkoFV and Yurchee like this.
  11. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    Exactly, this is what I do most of the time, if I can hide the visual by one frame and still be ok with it.

    BUT!!!!

    My other programmer part screams that "THIS IS NOT THE PROPER WAY TO DO IT" Why on earth do I have to wait for next frame for the UI layout to be updated? Single frame wait can sometimes makes a whole difference at least how unnecessary it adds the complexity to the code and flow of the logic.

    Wait one frame????? NEVER!
     
  12. Overcast

    Overcast

    Joined:
    May 16, 2013
    Posts:
    29
    Yea agree with all this, Unity's UI has sucked since it began, strategically they abandoned this implementation of the UI system years ago (when the UIElements one was still a pipe dream), after only a couple of years in production. I would be very surprised if this is ever fixed before they make the next one default and legacy this.
     
  13. MaxLohMusic

    MaxLohMusic

    Joined:
    Jan 17, 2022
    Posts:
    67
    Same problem here and the deep refreshing layout including the children doesn't always work. I call it and the height is still wrong, then magically in the next frame, it's correct. Then I have to do a dumb hack with a variable literally named "doDumbUnityThing" which is set to true so that the next Update() can get the REAL height.
     
  14. Tomsterk

    Tomsterk

    Joined:
    Nov 23, 2012
    Posts:
    10
    I am going to become the joker
     
  15. bab202

    bab202

    Joined:
    Oct 18, 2018
    Posts:
    15
    This is the complete answer for refresh LayoutGroups.
     
    noonoox likes this.