Force Immediate Layout Update

works for me :smile:

3 Likes

^ for me that one didnt work on 2018.3.x…

this one did,

1 Like

It all makes sense. If you’re still having issues, you’re building and managing your layouts improperly.

Re: Stephan_B’s solution w/ LayoutBuilder, if it’s not working for anyone, make sure that you pass in an object with a layout component attached to it. Targeting an object higher up in the hierarchy but without a layout component doesn’t work.

1 Like

Hi! I tried your script and it does not do anything. I have VerticalLayout and ContentSizeFitter with prefferedHeight in 3-4 levels. I have tried putting your script everywhere but nothing works. The intent was to prevent my tooltip from flickering while it autoresizes and because its size changes, have it unaligned when it finishes autoresizing.

Would be cool if you merge the base ContentSizeFitter with your CustomSizeFitter and have a single solution

I somehow got it working in Play mode, then trying to set it for real it doesn’t work anymore.
Then I half-got it working afterwards. I wish it would behave like ContentSizeFitter in editor mode. Now all my hierarchy looks overlapped and messy even though it sort of works in Play mode. I am sure I didn’t remove a ContentSizeFitter somewhere.

If you are looking at an upgrade, then overriding the original ContentSizeFitter’s functionality of vertical autosizing (in edit time) would be a great boost!

What license are you releasing this under?

This is correct answer for me :smile:

this worked for me though applied to a GridLayoutGroup, thanks a bunch :slight_smile:

It worked. Thanks!

Just to chime in that this was the fix for me.

LayoutRebuilder.ForceRebuildLayoutImmediate is misleading. Its first parameter is called layoutRoot, but in fact, you must call it on each LayoutGroup, not on a root object that contains LayoutGroups.

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

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

This is false and should probably be fixed in the documentation.

16 Likes

I’d replace Get Component with an array or something

Thanks, this solved the issue for me:
I was using a horizontal layout group to have even spacing between neighbouring rect transforms. When one of the rect transforms’ width was modifyed via code, the layout group did not adjust accordingly to keep the spacing even.

1 Like

Same here, I guess it should adjust its width and spacing automatically but it’s not happening, what’s the point of layouts if they do not update automatically?

They usually rebuild automatically. However of you are using nested contentfitters you might get the problem that the parent fitter updates before the child (I made an example in this post )

2 Likes

I can’t find LayoutBuilder in neither unity documentation nor import in my project.
What is the current API for forcing relayout now?

it lives in that namespace: UnityEngine.UI (I’m using Unity 2019.4.16f1)
Try it liket that:

UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(rootRectTransform);

Thanks, I’ve already used Canvas.ForceUpdateCanvases(). The drawback of it is, it updates the whole canvasses and rectransforms inside them which is a big perfomance drop.

2 Likes

awesome solution. Thank you!

2 Likes

Instead of grabbing the layout and forcing that to update, you could just steal a line from LayoutElement (which never seems to have any problem like this) - LayoutRebuilder.MarkLayoutForRebuild(transform as RectTransform) which achieves the same effect and doesn’t require you to find or store anything extra, as people have done above.

Seemed to work for me with a custom component which implements ILayoutElement that wasn’t updating before. Don’t know why nobody mentioned this, given that LayoutRebuilder.ForceRebuildLayoutImmediate was mentioned.

Chiming in to the discussion above. After grappling with the answers above, I THINK that I’ve finally figured out why my code was working with some of the solution above, and it wasn’t for others.

Here are my basic understanding of this topic

  1. The UI items are only rendered in the frame, therefore, in order to get the most accurate layout size, any calculation can only be done AFTER the relevant child objects have been added to the layout group.
  2. This also means that the gameobject containing the VerticalLayoutGroup MUST BE ACTIVE IN THE SCENE before calling ForceRebuildLayoutImmediate, and waiting for the end of frame.

For testing, since my code was already working, I did a test and found out point 2, which is the point that I think many people MIGHT be missing.

levelSelectContent contains the VerticalLayoutGroup, while menuPanelObject[2] is the root gameobject. As you can see in my code, I simply enabled it, and it was able to get the correct size delta for me.

        var rectTransform = levelSelectContent.GetComponent<RectTransform>();
        LayoutRebuilder.ForceRebuildLayoutImmediate(rectTransform);
        yield return new WaitForFixedUpdate();
        var anchoredPosition = rectTransform.sizeDelta;
        Debug.Log("SizeDelta: " + rectTransform.sizeDelta);
        menuPanelObject[2].SetActive(true);
        menuPanelObject[2].GetComponent<CanvasGroup>().alpha = 0.0f;
        LayoutRebuilder.ForceRebuildLayoutImmediate(rectTransform);
        yield return new WaitForFixedUpdate();
        Debug.Log("SizeDelta: " + rectTransform.sizeDelta);

7523492--928367--upload_2021-9-26_14-9-36.png

7523492--928364--upload_2021-9-26_14-8-37.png

This works for me… but only if the canvas is active in the hierarchy when the scene is opened. I’m not sure if anyone else sets up their scenes that way (I have multiple canvases in a scene, and while I’m working on one I deactivate the others). I guess there’s some sort of initialisation process run on canvases or on LayoutGroup when a scene is opened, that isn’t being run if it’s inactive?