Reputation: 664
I am creating toggle buttons programmatic in Unity at run time, and I would like to add an event to happen to these toggles.
I want to add to the Unity Event that is triggered when the toggle is clicked, but I can't seem to tie it together properly.
public Toggle myToggle_Prefab;
pulic int numberToggles;
private Toggle[] runtimeToggles;
private void Start()
{
runtimeToggles = new Toggle[numberToggles];
for(int i = 0; i < runtimeToggles.Length; i++)
{
runtimeToggles[i] = Instantiate(togglePrefab);
// ADD TO THE EVENT HERE
runtimeToggles[i].onValueChanged.AddListener(MyMethod);
}
}
private void MyMethod(int index){
// Do something
}
That doesn't work out because it is an integer paramater. The method works with a bool parameter like this:
private void MyMethod(bool arg0)
{
// Do stuff
}
But I need the integer parameter to do what I want to do. Any thoughts?
Upvotes: 1
Views: 6157
Reputation: 37
I'm also new to unity and coming from other languages with Event Systems, the approach is roughly the same, just a difference in how to go about it.
I too have an array of Toggles that I not only wanted to know when one was selected but also WHICH one dispatched the event without having to write code to iterate through array indexes or other solutions. My Toggles were already in the prefab, but I will be moving to Instantiating prefabs for a dynamic list. Other than the the setup is the same and one can pass the toggle to the handler like so:
private void OnEnable()
{
_tabs = GetComponentsInChildren<Toggle>();
// Loop through the toggles to add listeners.
for (int i = 0; i < _tabs.Length; i++)
{
int idx = i;
_tabs[idx].onValueChanged.AddListener(delegate
{
TabClickHandler(_tabs[idx]);
});
}
}
private void OnDisable()
{
// Loop through toggles to remove listeners...
for (int i = 0; i < _tabs.Length; i++)
{
// As mentioned above, you need a local variable to pass to the delegate.
int idx = i;
_tabs[idx].onValueChanged.RemoveListener(delegate
{
TabClickHandler(_tabs[idx]);
});
}
}
public void TabClickHandler(Toggle tab)
{
// State changes are handled here which includes the previous toggle being set to off. Therefore, filter for only the isOn == true state changes
if (tab.isOn)
Debug.Log("Tab: " + tab.name + " isOn State:" + tab.isOn);
// Do any stuff you want here. You now can identify the tab. In my case it was to start the load process for a different inventory classification list.
}
Just make sure to remove your listeners to avoid memory leaks. If you are familiar with event systems, a lot of other languages also require this.
Upvotes: 0
Reputation: 15941
You do it like this:
for(int i = 0; i < runtimeToggles.Length; i++)
{
runtimeToggles[i] = Instantiate(togglePrefab);
// no-parameter version:
runtimeToggles[i].onValueChanged.AddListener(delegate{ MyMethod(thatInteger); });
// boolean parameter version:
runtimeToggles[i].onValueChanged.AddListener(delegate (bool b){ MyMethod(thatInteger); });
}
Wrap the method you want to call in a delegate (which takes either no parameters or the boolean) and call your method from there. Note that I've left thatInteger
undefined, as your question doesn't indicate what the value is or is for.
Do note that if you want to pass the i
value, you will need to copy it to a local-scope variable (See this question for why) first:
for(int i = 0; i < runtimeToggles.Length; i++)
{
// like this:
int thatInteger = i;
runtimeToggles[i] = Instantiate(togglePrefab);
// no-parameter version:
runtimeToggles[i].onValueChanged.AddListener(delegate{ MyMethod(thatInteger); });
// boolean parameter version:
runtimeToggles[i].onValueChanged.AddListener(delegate (bool b){ MyMethod(thatInteger); });
}
The boolean, by the way, I believe represents what the toggle was changed to (i.e. if it becomes checked, then that boolean passed is true
).
The reason it works is because the event can invoke a narrow range of predefined delegate methods (offhand those signatures would be ()
, (bool state)
, and (UI.Toggle t)
and I'm not 100% sure about that last one) covering the most probable uses of the event. Anything else has to get wrapped in a delegate that conforms to one of those; e.g. delegate{ f(int i); }
, as otherwise the Unity internals don't know what it is you want to pass to your method.
No, the official documentation doesn't list these signatures (unhelpful, that) and are invoked through internal voodoo magic (like a lot of Unity MonoBehaviour methods).
Upvotes: 4