Instatiating UI elements in code;

I’ve been playing with UI elements in scripts. I think I’m getting the methods down… but the problem is that I don’t know how to instantiate them.

I get my import statement in:
using UnityEngine.UI;

I create a member variable:
Toggle[ ] toggles = new Toggle[16]; Button makeChoice;

And that’s all good… But I get a null error when I try to do things with toggles and makeChoice later. Which totally makes sense - I need to initialize my objects.
But then toggles = new Toggle() and makeChoice = new Button() throw me an error - the constructor is private and I can’t touch it. I’ve searched around and I haven’t been able to find anything.
How can I initialize my UI objects?

These are components. So, like any other component in Unity, you have to create a new GameObject and then add them with AddComponent. For example:

GameObject noob = new GameObject();
toggles[0] = noob.AddComponent<Toggle>();

Then you would have to go through and configure them, including the sub-objects, getting the font for the label, etc. I’ve done it, but it’s definitely the Hard Way. In almost all cases, you should instead simply make a prefab of each kind of control you need, and then just instantiate the prefab.

2 Likes

Thanks for responding again!

This is definitely a step in the right direction. I definitely understand the problem with instantiating all of the fields of a Toggle. A set prefab makes a lot of sense.

I’m just having a bit of trouble getting that to work. I made a Toggle prefab by adding a Toggle to the hierarchy, creating a new prefab (called “ToggleFab”), dropping the Toggle in, and deleting it from the hierarchy… but my code acts like it doesn’t exist when I try to say something like toggles.Instantiate(ToggleFab).

EDIT: Upon further inspection, it seems that I have to declare “ToggleFab” at the top of the script like a variable… but I’m not sure quite how. The example is “public Transform brick;” but a UI Prefab isn’t a Transform.

I’m also still trying to get the static thing to work: I want this script to run immediately upon starting the game. The problem is that I can only create an Object in Start(). But with the static call, it runs the static constructor first, ignoring Start(). And then it gets mad if I try to add a component to my undefined dummy object.

You might be missing using UnityEngine.UI

This may also help

Yes, it sounds like you need to spend a little more time with the tutorials. Instantiating objects from prefabs is a core Unity skill; almost any entry-level tutorial ought to cover it, and once it “clicks” for you you’ll be in good shape.

But let me see if I can help.

Already it sounds like you’re working a little harder than you need to here. My usual pattern would be (1) create a Toggle in the hierarchy, (2) drag this to the Project tab (which creates a prefab and turns the one in the hierarchy into a prefab instance, and (3) delete the instance in the hierarchy.

So, it’s almost the same as what you’re doing, but one step shorter and I think may avoid an extra layer in your prefab.

Sure it is. Or more precisely, it has a Transform, and you can instantiate via that. However, this is a little indirect, and not how I would normally do it. I know it’s what the docs show, but I find it less obtuse to declare my property as type GameObject, and instantiate via that. Something like:

public class Demo : MonoBehaviour {
    public GameObject prefab;

    void Start() {
        GameObject noob = Instantiate(prefab) as GameObject;
    }
}

And then you can call noob.GetComponentInChildren<UnityEngine.UI.Text>() to get a text component somewhere within it, or whatever you need.

Yep. Take out the static initializer. Do all your startup work in Awake and/or Start.

You can go one step further and just Instantiate the Text. No GetComponent required.

You can if the thing you’re instantiating is itself a Text object, and not (say) a Toggle or Button that has a Text object as a child (thus my use of GetComponentInChildren).

1 Like

Thanks for all the help! I’m so close to getting this right!

My script still doesn’t recognize the prefabs I’ve made though.

Again, I have a Toggle and button prefab that I’ve called ToggleFab and ButtonFab respectively, and I dragged them to the GameObject in the hierarchy that holds the script I’m using.

I’ve instantiated

public GameObject ToggleFab;
public GameObject ButtonFab;

at the top, and later on I say

GameObject[] dummy = new GameObject[17];
for (int i = 0; i < dummy.Length - 1; i++) {
     dummy[i] = Instantiate(ToggleFab) as GameObject;
}
dummy[dummy.Length - 1] = Instantiate(ButtonFab) as GameObject;

But then I get issues with ToggleFab and ButtonFab not being initialized.

“Not initialized” in what way? And can you post a screen shot of your setup, showing the script on your object with the properties filled in?

Scratch that. I got my elements to appear… kinda. They’re there, but they’re totally invisible.

In Start() I have:

GameObject[] dummy = new GameObject[17];
for (int i = 0; i < dummy.Length - 1; i++) {
     dummy[i] = Instantiate(ToggleFab) as GameObject;
}
dummy[dummy.Length - 1] = Instantiate(ButtonFab) as GameObject;
dummy[dummy.Length-1].GetComponent<Button>().transform.position = new Vector2(450, 100);
dummy[dummy.Length-1].GetComponent<Button>().GetComponentInChildren<Text>().text = "Make Selection";

and then later on in start, in a for-loop that runs over the ToggleFabs in dummy:

dummy[blah].GetComponent<Toggle>().transform.position = new Vector2();
dummy[blah].GetComponent<Toggle>().GetComponentInChildren<Text>().text = "blah";

Ignore the for-loop logic, lack of coords in vector2, and the uninteresting text. I set those properly through the for loop’s logic. The invisible boxes appear in the right locations and they hold the right text, even they don’t display it.

EDIT: I’ll look into OnGUI. I totally forgot about that. For some reason I thought I wouldn’t have to use that for my implementation, but I totally do.

Ah, yes, UI elements don’t draw unless they’re inside a Canvas. So, if your script is on the Canvas, you can just add

dummy[i].transform.SetParent(transform);

Otherwise, you’ll need a “public Canvas canvas” property in your class, and use that to set the parent to canvas.transform instead.

NO, you totally don’t. Trust me, I have games that still use OnGUI, but I wish I didn’t. Don’t choose the way of pain.

1 Like

Haha, having everything appear for the first time was so satisfying! Thank you so much! I’m so glad I don’t have to touch OnGUI either… I caved and tried that earlier, and yeesh is it ugly.

Now, how do I access the toggle itself?

dummy_.GetComponent().isOn and dummy*.GetComponentInChildren().isOn are both giving me null issues.*_

Well you’re on the right track. GetComponent (or GetComponentInChildren) will do it, if you call it on a reference to something that has a Toggle component (or has a child with a Toggle component). Just take it step by step and try to think carefully about what you’re doing. For example:

dummy[blah].GetComponent<Toggle>().transform.position = new Vector2();
dummy[blah].GetComponent<Toggle>().GetComponentInChildren<Text>().text = "blah";

In Line 1, the GetComponent call is pointless. This line says “OK, take this GameObject dummy[blah], which has a transform and a bunch of other components, and find its Toggle component, and then from that find its transform, and change its position.” Instead, just access dummy[blah].transform directly.

And in Line 2, basically same thing. You’re saying “Take this GameObject dummy[blah], find its Toggle component, then forget about that and find instead the Text component in one of its children.”

I feel I’m not explaining this very well, so let me know if this is clear. The key thing is to have a mental model: things in Unity are made up of GameObjects which contain Components. In particular, every GameObject has a Transform, and every Transform has a GameObject; there is a very tight 1:1 relationship between Transforms and GameObjects.

.transform, when called on any component, are just a shortcut for accessing the GameObject that component is on, and getting its Transform. And GetComponent, when called on any component, gets another component on the GameObject that component is on.

So if you have a GameObject Foo, with components FooA and FooB, then Foo.transform, FooA.transform, and FooB.transform are all the same thing. Likewise, Foo.GetComponent(), FooA.GetComponent(), and FooB.GetComponent() are all the same thing. It doesn’t matter how you access the game object and do something with it — it’s only one game object (just with multiple components).

HTH,

  • Joe
1 Like

I think I understand, but I’m not too sure. The part about making the two lines of code you mention less redundant makes sense.

So, in that other method I wrote, which originally looked like:

int toReturn = 0;
        for(int i = 0; i < dummy.Length-1; i++)
        {
            if(dummy[i].GetComponent<Toggle>().isOn) toReturn++;
        }
        return toReturn;

I also tried to mimic the documentation’s example here to no avail.

I played around a bit and added

if(dummy[i].GetComponentInChildren<Toggle>() == null) print("I don't exist?");

Which didn’t print. I guess that’s one of the more confusing parts to me - the documentation says that the GetComponent call should return null if there is no component, but that line won’t run without throwing an error.

(Sidenote: When I refer to “dummy” in this post, I’m totally ignoring the ButtonFab at the array’s end)

So I guess the main takeaway from those trials is that looking for the Toggle by calling GetComponent on an element in dummy[ ] isn’t going to cut it at all.

Let me think… so dummy[ ] is an array of ToggleFabs Initialized as GameObjects. And ToggleFab itself either is or contains a Toggle component. So calling GetComponent on a ToggleFab is going to be a bit weird?

I found the solution! I have to find the togglecomponents in the children of canvas! I get why this works, but I’m not 100% sure why my previous methods didn’t. I guess it has to do with the relationship between ToggleFab and the Toggle itself?

Thank you so much for all the help! I have a better understanding of what I’m doing.

It’d be much easier to help if you posted a screenshot of your setup. I infer from the evidence you presented that ToggleFab does not have a Toggle component, but perhaps contains some child object that does have it. Note that you used GetComponent in the line that didn’t work, but used GetComponentInChildren in the line where you checked for null (and didn’t find it). So, that must be the case — it’s not on the dummy object, but on some child.