Home > Software design >  Switch other toggles when one of it is on
Switch other toggles when one of it is on

Time:09-22

I have an array of toggles defined in script. They are all turned off in the beginning. When the user clicks on one of the toggles, that toggle should be turned on and the other toggles should be switched to off state. Basically there could be only "one" on state toggle. How do I achieve this?

Currently with this script, all the toggles are getting turned off when the user clicks on one of it.

public Toggle[] toggle;

 void Start () {
        for (int i = 0; i < toggle.Length; i  ) {
            int idx = i;
            toggle[idx].onValueChanged.AddListener (delegate {
                ToggleValueChanged (toggle[idx], idx);
            });
        }
    }

    public void ToggleValueChanged (Toggle change, int index) {
       
        for (int i = 0; i < toggle.Length; i  ) {
            if (i == index) {
                return;
            } else {
                if (toggle[i].isOn) {
                    toggle[i].isOn = false;
                }
            }
        }

    }

CodePudding user response:

Unity has a component called ToggleGroup that allow you to do just that.

ToggleContainer Parent Object:

ToggleContainer GameObject

Reference ToggleGroup object in every Toggle component as following:

enter image description here

Scene Hierarchy:

Scene Hierarchy

Overview:

https://gfycat.com/bossyscenteddorado

CodePudding user response:

Change your ToggleValueChanged function like this :

public void ToggleValueChanged (Toggle change, int index) 
{
    for (int i = 0; i < toggle.Length; i  ) 
    {
        if (i == index) continue;
            
        if (toggle[i].isOn) {
            toggle[i].isOn = false;
        }
    }
}

When you return in you first if statement, other toggles won't get off. you have to continue iterating your loop. And instead getting the index and passing it to the delegate, you can use RefrenceEqual

EDIT Actually each time you manipulate the toggle[i].isOn, you are changing the value of it. So each time, You are calling your function. EDIT
try this :

public void ToggleValueChanged (Toggle change, int index) 
{
    for (int i = 0; i < toggle.Length; i  ) 
    {
        if (i == index) continue;
            
        if (toggle[i].isOn) 
        {
            toggle[i].SetIsOnWithoutNotify(false);
        }
    }
}

CodePudding user response:

Why do you even need the index for this?

Simply do

 public void ToggleValueChanged (Toggle change) 
 {
     // add a little safety to actually only disable all others if 
     // this one ws actually enabled
     if(!change.isOn) return;

     foreach(var toggle in toggles)
     {
         if (toggle == change) continue;
         
         if (toggle.isOn) 
         {
             // I would actually specifically NOT use "SetIsOnWithoutNotify"
             // because you never know who else is actually listening to the state change
             // so if you simply set it to false but don't invoke the callback events
             // things might behave different than expected
             toggle[i].isOn = false;
         }
    }
}

and accordingly

foreach(var t in toggle)
{ 
    var currentToggle = t;
    currentToggle .onValueChanged.AddListener(value => ToggleValueChanged(currentToggle));
}

CodePudding user response:

There is no need to either return or continue, just don't handle the toggle if it's the indexth one:

public void ToggleValueChanged (Toggle change, int index) 
{
    for (int i = 0; i < toggle.Length; i  ) 
    {
        if (i != index) 
        {
            toggle[i].isOn = false;
        }
    }
}

Also you can assume that all others are off, so no reason to check that either.

CodePudding user response:

If they are mutually exclusive, they aren't "boolean states", it's just one state, supported by an enum and including the null value. If your UI is a set of checkboxes, you should switch it to a set of radio buttons. A dropdown box would do as well.

  • Related