Get calculated preferredHeight after content change

I’m trying to create an accordion menu. It’s working nicely with preset content, but it’s not working with dynamic content.

The container is a vertical layout group with 2 text components and a button. The content within is inactive when it’s changed. To open it I make it active, and lerp the height of the container from 0 to it’s preferredHeight.

The trouble is that the preferredHeight value isn’t updating when I change the content. If I change the content but deselect “Preferred Height” in the Layout Element component it is the correct size, but then it doesn’t lerp.

If I manually deselect and reselect “Preferred Height” in the Layout Element component, the preferredHeight value changes. However I can’t seem to figure out how to accomplish the same thing in code.

I’ve tried Layoututility, which seems to be made for just this, but it doesn’t work either.

I’m using 5.3.4

Try using Canvas.ForceUpdateCanvases to force the canvas to redraw after content change.

I’ve got it working by calculating and getting the preferred height from the vertical layout group:

void OnEnable()
    {
        Canvas.ForceUpdateCanvases();
        GetComponent<VerticalLayoutGroup>().CalculateLayoutInputVertical();
        preferredHeight = GetComponent<VerticalLayoutGroup>().preferredHeight;
        paddingTop = GetComponent<VerticalLayoutGroup>().padding.top;
        paddingBottom = GetComponent<VerticalLayoutGroup>().padding.bottom;

        GetComponent<LayoutElement>().preferredHeight = 0f;
        GetComponent<VerticalLayoutGroup>().padding.top = 0;
        GetComponent<VerticalLayoutGroup>().padding.bottom = 0;

    }

Then I use the preferredHeight of the layout element to scale the group open and closed:

public IEnumerator Open(float time)
    {
        float elapsedTime = 0;
        
        while (elapsedTime < time)
        {
            this.gameObject.GetComponent<LayoutElement>().preferredHeight = 
                Mathf.Lerp(this.gameObject.GetComponent<LayoutElement>().preferredHeight, preferredHeight, elapsedTime / time);

            this.gameObject.GetComponent<VerticalLayoutGroup>().padding.top = 
                (Int32)Mathf.Lerp(this.gameObject.GetComponent<VerticalLayoutGroup>().padding.top, paddingTop, elapsedTime / time);
            this.gameObject.GetComponent<VerticalLayoutGroup>().padding.bottom =
                (Int32)Mathf.Lerp(this.gameObject.GetComponent<VerticalLayoutGroup>().padding.bottom, paddingBottom, elapsedTime / time);

            elapsedTime += Time.deltaTime;

            yield return null;
        }
    }