Jack Sullivan
Jack Sullivan

Reputation: 65

Can't fit parent Canvas to child Text

In my unity application, I need make little rectangular components with a string on them. However, I want the size of the rectangle to dynamically line up to the text inside it. This is the hierarchy in my prefab:

Prefab Hierarchy

According to this article, I just need to add a content fitter to the canvas. I did that and set both horizontal and vertical sizes to "preferred", however apparently the width is just 0 (0 is getting printed out by my Debug.Log). This is my code. I think the issue might have to do with changing the render mode, but this is for a VR application so that needs to stay:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Script : MonoBehaviour
{
    public static string s_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";

    public Transform panel;
    public GameObject wordPrefab;

    private float panelWidth;
    private float panelHeight;
  
    // Start is called before the first frame update
    void Start()
    {
        
        panelWidth = panel.GetComponentInParent<Canvas>().GetComponent<RectTransform>().rect.width;
        panelHeight = panel.GetComponentInParent<Canvas>().GetComponent<RectTransform>().rect.height;

        Vector3 nextPosition = new Vector3(-panelWidth / 2, panelHeight / 2, 0);

        foreach (string word in s_text.Split(' '))
        {
            GameObject go = Instantiate(wordPrefab, panel, false);
            go.GetComponentInChildren<Canvas>().renderMode = RenderMode.WorldSpace;
            go.GetComponentInChildren<Text>().text = word;
            go.transform.localPosition = nextPosition;

            float wordWidth = go.GetComponentInChildren<Canvas>().GetComponent<RectTransform>().rect.width;
            Debug.Log(wordWidth); // This is 0 every time when it shouldn't be!

            nextPosition = 0; // I will figure this out once I can access the size of the blocks
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

TL;DR: I need to get the parent canvas to size correctly, and for some reason its width/height are 0 after doing exactly what the linked article said and using a content fitter.

Upvotes: 1

Views: 2852

Answers (1)

TEEBQNE
TEEBQNE

Reputation: 6275

I believe this should answer your question - if you have more issues with this implementation, just let me know.

Firstly, remove all code with resizing. Unity's component UI system is very powerful when used properly. Almost anything you would want to achieve with dynamic UI can be done using just components.

Firstly, your container holding the text should have a Horizontal Layout Group or a Vertical Layout Group attached to it. Turn off all toggle boxes except for Control Child Size - Height. Set the width of this container to whatever you want the width to be, the text will fill to this size.

Along with a layout group (you can choose either one, but as your text is vertical you can use a vertical layout group), you will need a Content Size Fitter with the Horizontal Fit set to Unconstrained and the Vertical Fit set to Preferred Size.

It should look something like... Example container

Now moving onto the text component, you can use a Unity.UI.Text component or a TMPro.TMP_Text component, either one will work. In this example, I am using the vanilla Unity.UI.Text. Simply type whatever message you would like in the text field and your parent container should resize to the text field. If you are dynamically setting this text often, you might need to force Unity to rebuild the layout group of your parent object to rescale.

An example snippet of this could look like...

using UnityEngine.UI;

// the text object that is resizable
[SerializeField] private Text dynamicText = null;

// the parent container that holds the text
[SerializeField] private RectTransform dynamicTextParent = null;

public void UpdateTextField(string txt)
{
    // assign the new text info
    dynamicText.text = txt;
    
    // force Unity to recalculate the UI hierarchy of this object recursively down its child nodes
    LayoutRebuilder.ForceRebuildLayoutImmediate(dynamicTextParent);
}

In this example, you would need to assign dynamicTextParent and dynamicText in the inspector. There is also a chance that you will not need to forcibly rebuild the UI hierarchy, so test it without first. If you run into issues, add the LayoutRebuilder under the line where you assign the new text field.

Upvotes: 2

Related Questions