Why does toggle invoke onValueChanged callback even when its isOn property actually not changed

hello,
I just found that my toggle object always fired its onValueChanged callback everytime it was clicked, even when its isOn was already true and had never been changed to false.
To confirm this, I wrote the following testing script:

using UnityEngine.UI;
using UnityEngine;

public class ToggleTest : MonoBehaviour {
	public Toggle myToggle;

	public void Start() {
		myToggle.onValueChanged.AddListener(delegate (bool isOn) {
			if (isOn)
				Debug.Log("is On");
			else
				Debug.Log("is Off");
		});
	}
}

I attached the above test script to objects with Toggle components, and Play Unity. When I kept clicking just ONE SINGLE toggle object several times, the console kept printing “is On” repeatedly, without “is Off” among them, suggesting that the toggle’s isOn property had NEVER been changed and onValueChanged callback was fired just because the toggle was CLICKED.
This is not the way ON VALUE CHANGED should work in my opinion. I tryed searched but found no answer or similar questions.
Can anyone figure out why? Thanks!

1 Like

It would make more sense to just log the value of isOn.

And also to log the actual Toggle.isOn property to see if they match.

Thank you! You are right. So I modified my script to the following:

using UnityEngine.UI;
using UnityEngine;

public class ToggleTest : MonoBehaviour {
	public Toggle myToggle;

	public void Start() {
		myToggle.onValueChanged.AddListener(delegate (bool state) {
			Debug.Log("Debug Start:");
			Debug.Log("toggle.isOn = " + myToggle.isOn);
			Debug.Log("state = " + state);
			Debug.Log("Debug Finished.");
		});
	}
}

However, the problem still exists. When I keep clicking the same toggle button several times, the console outputs show that the toggle.isOn property and the callback parameter is always true, never changed. I cannot figure out why the onValueChanged callback is invoked at this situation. :frowning:

Oh I just found the reason, which suggests a bug of Unity’s UGUI.

My toggle buttons are bound to a toggle group, whose allowSwitchOff property is set to false, preventing the active toggle set to off. In this situation, I guess, the toggle.isOn is actually changed to false right after clicked (without onValueChanged fired), then the toggle group changes toggle.isOn back to true at once and wrongly invoked onValueChanged. And that is where the bug occurs I think.

To solve this problem, I wrote the following script to replace the default toggle component, where I block the onPointerClick event when the toggle is already active, preventing any later changes.

using UnityEngine.EventSystems;
using UnityEngine.UI;

public class MyToggle : Toggle {
	public override void OnPointerClick(PointerEventData eventData) {
		if (!group || group.allowSwitchOff || !isOn)
			base.OnPointerClick(eventData);
	}
}

And it works!