はじめに
Unityでラジオボタンを作るときに活躍するToggleGroup。
複数のToggleをグルーピングして、選択Toggleを択一(排他)にする機能を提供してくれます。
ところがこのToggleGroup、少し曲者です。
というのも、管理しているすべてのToggleを知る手段を提供していないのです。1
これは特に問題にならないことも多いですが、例えば、ToggleGroupから選択肢一覧を知りたいときなどに困ります。(結局自分で別管理することになったり…。)
今回はそんなときにどうするのがよいか、考えてみます。
案
主に2つの方法が考えられます。
1. Toggle側からアプローチする
Toggleはgroupという自身が所属するToggleGroupを表すプロパティをもっています。
それならば、シーンにあるToggleを集めてそのgroupを調べればいい…。例として下記のようなコードで実現できます。
// using System.Linq;が必要
/// <summary>
/// ToggleGroupに所属するToggleを取得
/// </summary>
/// <param name="toggleGroup"></param>
/// <returns></returns>
private IEnumerable<Toggle> GetTogglesOf(ToggleGroup toggleGroup)
{
var toggles = GameObject.FindObjectsOfType<Toggle>();
return toggles.Where(x => x.group == toggleGroup);
}
この取り方はToggleGroupからのアプローチでない故の分かりにくさが欠点かもしれません。
あとは検索コストがかかるため少し無駄を感じます。
2. リフレクションで取り出す
無理やり何とかしたいときの常套、リフレクションです。
実はuGUIのソースコードはBitbucketで公開されているため、内部でどうToggleをもっているかを知ることができます。
該当のソースコードを見てみるとList<Toggle>
型の**m_Toggles
**という名前でprivateフィールドでもっていることが分かりました。
フィールド名が分かってしまえば、下記のような拡張メソッドを作ることでいけます。2
using System.Collections.Generic;
using System.Reflection;
using UnityEngine.UI;
/// <summary>
/// トグルグループ拡張
/// </summary>
public static class ToggleGroupExtensions
{
/// <summary>
/// ToggleGroupがもつToggleのFieldInfo
/// </summary>
private static FieldInfo _togglesFieldInfo = null;
static ToggleGroupExtensions()
{
_togglesFieldInfo = typeof(ToggleGroup).GetField("m_Toggles", BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
if (_togglesFieldInfo == null)
{
throw new System.Exception("Not compatible with the current version of ToggleGroup.");
}
}
/// <summary>
/// トグル一覧を取得
/// </summary>
/// <param name="self"></param>
/// <returns></returns>
public static IEnumerable<Toggle> GetToggles(this ToggleGroup self)
{
return (_togglesFieldInfo.GetValue(self) as List<Toggle>);
}
}
使うときはこんな感じになります。
foreach (var item in _toggleGroup.GetToggles())
{
Debug.Log(item.name);
}
なお、注意点として、リフレクションの性質上ToggleGroupの内部実装が変わると互換性を失う可能性があります。
おわりに
今回紹介した方法はどちらも素直なやり方という感じはしませんね。
どうせ機能拡張などを見越しているなら、自分でToggleGroupに代わる実装するのがベストなのかもしれません。(中身見れるので参考にもできますしね…。)
-
もちろん選択されているToggleは取得できます(ActiveToggles)。でもなんで戻り値が単一じゃなくコレクションなんだろう…? ↩
-
本当はToggleGroup.togglesのようにプロパティで書きたいところですが、現時点(C#7.3)には拡張プロパティはないらしいです。 ↩