Search Issue Tracker

Fixed in 2019.3.X

Fixed in 2019.1.X, 2019.2.X

Votes

28

Found in

2019.1.0a5

2019.1.0f2

Issue ID

1146883

Regression

Yes

Re-enabling game object with "Toggle group" loses information about previously checked toggle

uGUI

-

Steps to reproduce:
1. Open attached project "Test.zip"
2. Go into play mode
3. There are 3 toggles at the top, select a red one (row of red toggles should appear on the left side)
4. Check the first toggle on the left side row (You can check any toggle, but remember your choice)
5. Press green toggle (from 3 toggles at the top)
6. Press red toggle again (from 3 toggles at the top)
-The first toggle is not selected anymore from the left row, instead, another toggle is checked randomly. (Attached a video)

Explanation: Toggles at the top disable/enable "Toggle group" (only a single game object with toggle group is active at a time)

Expected result: After re-enabling element with "Toggle group" it shouldn't uncheck previously checked toggle
Actual result: Re-enabling game object with "Toggle group" loses information about previously checked toggle and instead checks random toggle in the group

Note: Sometimes it takes a couple of tries to reproduce the bug (repeat steps from 3rd step)

Reproduced on: 2019.1.0a5, 2019.1.0a6, 2019.1.0a11, 2019.1.0f2, 2019.2.0a1, 2019.2.0a10
Doesn't reproduce on: 2017.4.0f1, 2018.3.13f1, 2019.1.0a1, 2019.1.0a3, 2019.1.0a4
Regressed in: 2019.1.0a5

  1. Resolution Note (fix version 2019.3):

    By default, ToggleGroup is coded to activate/deactivate the container for all associated Toggle elements. When the container is deactivated, all contained Toggles will get their OnDisable called. This will cause the Toggle to unregister itself from the ToggleGroup.

    When a Toggle calls UnregisterToggle() on it's ToggleGroup, it is removed from the ToggleGroup's internal list.

    Previously, any time UnregisterToggle() was called on a ToggleGroup that is set to disallow an off state, the group will then scan its list for at least one checked Toggle. Because the checked toggle is ultimately removed from the list before the list is totally emptied, the group will attempt to fix itself and set the first Toggle in the list to be On. This creates unpredictable behavior, both because the order of the list is non-deterministic, and because the first list element is rarely the value that should be set to On.

    This PR moves the logic for ensuring that one Toggle is marked On to a new function. The only times that this function actually needs to be invoked are:

    a) when the ToggleGroup gets its Start() method invoked. This will ensure that a ToggleGroup created in the Editor has a valid state when loaded.
    b) when a Toggle is deleted from the ToggleGroup. If the checked toggle is deleted programmatically or via the scene hierarchy, the list must choose a new one.

    New Toggles that are added programmatically will fix the state of the ToggleGroup as happened previously.

Comments (55)

  1. runette_unity

    Nov 13, 2022 12:07

    This still seems to happen in 2022.1

  2. leuconoe

    Oct 23, 2020 05:10

    It will recur on 2019.3.10.

    Problems when dragging a group of toggles after selecting multiple toggles in Hierarchy.
    If you add a toggle group to one toggle, and copy it, there is no problem.

  3. EdwinLyons

    Jul 16, 2019 15:43

    I can confirm this is fixed in 2019.1.10f1

  4. Panzerhandschuh

    Jul 13, 2019 01:48

    2019.1.10f1 fixed it.

  5. NobleRobot

    Jul 11, 2019 23:37

    Update (for anyone looking for a more lightweight workaround)

    Ultimately, all you need to avoid this issue is for "toggleGroup.allowSwitchOff" to be true before your "menu" is "disabled," which triggers the bug. This is a friggin' hassle to do, but since I already have delegates which are called when enabling and disabling the objects that contain my ToggleGroups, I can change allowSwitchOff to be false only while the ToggleGroup is active.

    This is dependent on my own "MenuController" class and its "onMenuEnabled/onMenuDisabled" delegates, so you'll have to adjust this to fit your codebase as needed, but conceptually, it's a drop-in solution that doesn't add a lot of overhead at runtime. I also set this script to run early in the project's Script Execution Order, just in case.

    (My actual version of this code populates the needed variables at *build-time* using IProcessSceneWithReport, so that I can just drop this on a object with a ToggleGroup without having to configure it while also avoiding any overhead (however tiny) in Start or Awake.)

    .........

    public class ToggleGroupBugFixer : MonoBehaviour {

    // This is to fix a dumb bug in Unity 2019.1.

    [SerializeField] private MenuController menu;
    [SerializeField] private ToggleGroup toggleGroup;

    private void Awake(){
    menu.onMenuEnabled += menuEnabledHander;
    menu.onMenuDisabled += menuDisabledHandler;
    }
    private void OnDestroy(){
    menu.onMenuEnabled -= menuEnabledHander;
    menu.onMenuDisabled -= menuDisabledHandler;
    }

    private void menuEnabledHander(){
    toggleGroup.allowSwitchOff = false;
    }
    private void menuDisabledHandler(){
    toggleGroup.allowSwitchOff = true;
    }

    }

  6. NobleRobot

    Jul 11, 2019 22:32

    CarlosFi's "failsafe" workaround basically works for me (thank you!!), but since I have other listeners checking onValueChanged, I end up getting stack overflows when the failsafe kicks in, so not ideal (ultimately I'll be wasting a few hours implementing and testing this or another workaround... ugh).

    This is a preposterous issue. Apart from the fact that "upgrading" to an alpha or beta version not being a serious solution, some of us are stuck on slightly older versions for 3rd-party SDK reasons which we have no control over, so this is a perfect example of why Unity needs to admit defeat and go back to issuing minor "f" releases to fix tiny but utterly game-breaking bugs like this.

    Even with a workaround in place, I don't think I can ship with a bug this silly.

  7. Panzerhandschuh

    Jul 10, 2019 21:55

    Updating to 2019.1.9f1 did not fix the issue. Hopefully next update will fix it.

  8. pointcache

    Jul 10, 2019 12:37

    "Fixed in 2019.1" which version exactly?
    In 2019.1.9f1 there is no word in changelog about this issue.

  9. CarlosFi

    Jun 26, 2019 10:59

    Sorry... my workaround code could be simplified even more removing some unecesary lines:

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

    public class ToggleGroupHelper : MonoBehaviour
    {
    private Toggle rememberMe;

    List<Toggle> source_list;

    private System.Reflection.FieldInfo _toggleListMember;
    ToggleGroup m_Group;

    void Start()
    {
    m_Group = this.GetComponent<ToggleGroup>();

    if(m_Group.allowSwitchOff)
    {
    return;
    }
    m_Group.allowSwitchOff = true;

    if (_toggleListMember == null)
    {
    _toggleListMember = typeof(ToggleGroup).GetField("m_Toggles", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
    if (_toggleListMember == null)
    throw new System.Exception("UnityEngine.UI.ToggleGroup Has become public, so this code must be changed!");
    }
    source_list = _toggleListMember.GetValue(m_Group) as List<Toggle>;
    if(source_list != null)
    {
    foreach(Toggle tt in source_list)
    {
    tt.onValueChanged.AddListener((bool on) => { ToggleChanged(tt, on); });
    }
    }
    }

    private void ToggleChanged(Toggle tt, bool on)
    {
    if (!on)
    {
    if(!m_Group.AnyTogglesOn())
    {
    tt.isOn = true;
    }
    }
    }
    }

  10. CarlosFi

    Jun 26, 2019 10:52

    ELADLEB4 workaround did not work well for me, so I coded another helper class that fixes the bug until 2019.3 version is available.
    Just create a class called ToggleGroupHelper and attach this script as a component to the same object where the ToggleGroup component is:

    [code=CSharp]
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;

    public class ToggleGroupHelper : MonoBehaviour
    {
    List<Toggle> source_list;
    List<Toggle> toggle_list = new List<Toggle>();

    private System.Reflection.FieldInfo _toggleListMember;
    ToggleGroup m_Group;

    void Start()
    {
    m_Group = this.GetComponent<ToggleGroup>();

    if(m_Group.allowSwitchOff)
    {
    return;
    }
    m_Group.allowSwitchOff = true;

    if (_toggleListMember == null)
    {
    _toggleListMember = typeof(ToggleGroup).GetField("m_Toggles", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
    if (_toggleListMember == null)
    throw new System.Exception("UnityEngine.UI.ToggleGroup Has become public, so this code must be changed!");
    }
    source_list = _toggleListMember.GetValue(m_Group) as List<Toggle>;
    if(source_list != null)
    {
    foreach(Toggle tt in source_list)
    {
    toggle_list.Add(tt);
    tt.onValueChanged.AddListener((bool on) => { ToggleChanged(tt, on); });
    }
    }
    }

    private void ToggleChanged(Toggle tt, bool on)
    {
    if (!on)
    {
    if(!m_Group.AnyTogglesOn())
    {
    tt.isOn = true;
    }
    }
    }
    }[/code]

Add comment

Log in to post comment

All about bugs

View bugs we have successfully reproduced, and vote for the bugs you want to see fixed most urgently.