- Home /
How to have a callback when a toggle is clicked in a toggle group?
I want a callback funcion to be called when one of my toggles in a toggle group is clicked.
I thought about making something like this
foreach (Toggle t in toggles) { t.onValueChanged.AddListener(callback); }
but then the callback would be called twice, because one toggle was selected, and other was deselected.
Any ideas?
Answer by thats_how_i_feel · Nov 26, 2015 at 11:41 AM
I made a class which inherits the ToggleGroup which has an OnChange
event: https://gist.github.com/jmbeach/78c3e46669db89628fce#file-bettertogglegroup-cs
using UnityEngine;
using UnityEngine.UI;
using System.Linq;
public class BetterToggleGroup : ToggleGroup {
public delegate void ChangedEventHandler(Toggle newActive);
public event ChangedEventHandler OnChange;
public void Start() {
foreach (Transform transformToggle in gameObject.transform) {
var toggle = transformToggle.gameObject.GetComponent<Toggle>();
toggle.onValueChanged.AddListener((isSelected) => {
if (!isSelected) {
return;
}
var activeToggle = Active();
DoOnChange(activeToggle);
});
}
}
public Toggle Active() {
return ActiveToggles().FirstOrDefault();
}
protected virtual void DoOnChange(Toggle newactive)
{
var handler = OnChange;
if (handler != null) handler(newactive);
}
}
Add the above class to your project and replace the ToggleGroup
script in the inspector with it. The class requires that all of the ToggleGroup's Toggles are children of it: To listen to the event, from another script use the following code:
TglGroup = GameObject.Find("Toggle Group Name").GetComponent<BetterToggleGroup>();
TglGroup.OnChange += TglGroup_OnChange;
//...
void TglGroup_OnChange(Toggle newActive)
{
Debug.Log(string.Format("Toggle {0} selected",newActive.name));
}
Cool. That might help people. I came up with a similar solution at the time.
Answer by DoomGoober · Mar 13, 2020 at 01:10 AM
@thatshowifeel had a good answer. Here's a slightly improved, more modern version:
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
[RequireComponent(typeof(ToggleGroup))]
public class EventToggleGroup : MonoBehaviour
{
[System.Serializable]
public class ToggleEvent : UnityEvent<Toggle> { }
[SerializeField]
public ToggleEvent onActiveTogglesChanged;
[SerializeField]
private Toggle[] _toggles;
private ToggleGroup _toggleGroup;
private void Awake()
{
_toggleGroup = GetComponent<ToggleGroup>();
}
// Start is called before the first frame update
void OnEnable()
{
foreach (Toggle toggle in _toggles)
{
if (toggle.group != null && toggle.group != _toggleGroup)
{
Debug.LogError($"EventToggleGroup is trying to register a Toggle that is a member of another group.");
}
toggle.group = _toggleGroup;
toggle.onValueChanged.AddListener(HandleToggleValueChanged);
}
}
void HandleToggleValueChanged(bool isOn)
{
if (isOn)
{
onActiveTogglesChanged?.Invoke(_toggleGroup.ActiveToggles().FirstOrDefault());
}
}
void OnDisable()
{
foreach (Toggle toggle in _toggleGroup.ActiveToggles())
{
toggle.onValueChanged.RemoveListener(HandleToggleValueChanged);
toggle.group = null;
}
}
}
To use:
1) Add EventToggleGroup as a sibling of ToggleGroup.
2) Drag Toggle into EventToggleGroup.toggles.
3) Add something to listen to onActiveToggleChanged. Note: It may return null if no toggles are selected!
Changes:
*Rather than assuming the Toggles are children of the ToggleGroup transform you must explicitly specify the toggles. This allows you to put the ToggleGroup/EventToggleGroup wherever you want in the GameObject hierarchy.
*It is not necessary to specify the "group" on the individual Toggle components anymore. (Note: This does allow a toggle to be a member of multiple EventToggleGroups! Don't do this.)
*Rather than deriving from ToggleGroup, EventToggleGroup is a sibling component of ToggleGroup. This conforms to the Entity/Component model better.
*Rather than raising a C# event, it raises a UnityEvent which allows us to hook up event listeners in the UnityEditor AND code.
Wow this is exactly what I needed! I don't really understand why there is not a class already built in Unity.
I think there is a typo line 49, shouldn't it be _toggles
instead of _toggleGroup.ActiveToggles()
?
Loving that answer as well. I added the functionality, that the Group also fires the invoke if every toggle gets turned of by modifying the HandleToggleValueChanged-Function and adding CheckIfAllOff:
void HandleToggleValueChanged(bool isOn)
{
if (isOn)
{
onActiveTogglesChanged?.Invoke(_toggleGroup.ActiveToggles().FirstOrDefault());
}
else
{
CheckIfAllOff();
}
}
void CheckIfAllOff()
{
bool allOff = true;
foreach (Toggle toggle in _toggleGroup.ActiveToggles())
{
if (toggle.isOn)
allOff = false;
}
if (allOff)
onActiveTogglesChanged.Invoke(null);
}
Answer by MarcoSoldati · May 21, 2021 at 08:48 AM
Great solution. Thanks. I added a small function to set one option to 'on' and disable the others (useful for radio buttons).
/**
* Programmatically set a toggle to on (and set all others to off).
*/
public void SetToggle(Toggle toggle)
{
foreach (Toggle t in Toggles)
{
bool isOn = toggle == t;
t.isOn = isOn;
Debug.Log(("setting " + t.name + " to " + isOn));
}
}
@DoomGoober line 49 it would be more failure safe to use _toggles instead of ActiveToggles()
Your answer
Follow this Question
Related Questions
Toggle Event passing bool [4.6+] 2 Answers
Is there a way to know when mouse is 'hovering' over a Toggle? 2 Answers
Is there an event other than OnValueChanged for Toggle UI? 0 Answers
Toggle's 'On Value Changed' checkbox is shown for bool parameter function 0 Answers
How do Pointer events work? 4.6 UI 1 Answer