TGViewer
SpellSinger - разработчик игр на Unity SpellSinger - разработчик игр на Unity @spellsingergames · 548 subscribers
Post #208 920
Как перестроить UI при включении/выключении дочернего элемента? Внедряемся в Layout.

Сегодня снова про UI. Ничто не отнимает у меня столько времени, как борьба с UI
.

Задача: сделать, чтобы в GridLayoutGroup ячейки были покрупнее, если элементов мало, и помельче, если их много.

В Unity есть коллбек OnTransformChildrenChanged, который вызывается, когда добавляется или удаляется новый дочерний элемент. Но этот коллбек не вызывается, когда дочерний элемент становится активен или неактивен.

Можно постоянно высчитывать количество активных дочерних элементовв Update, но это на самый крайний случай. Лучше не делать никакую логику в Update.

Можно повесить на дочерние элементы скрипт, который будет уведомлять родителя о своем включении/выключении, но это неудобно.

Предлагаю решение на основе методов SetLayoutHorizontal/SetLayoutVertical из интерфейса ILayoutController. Юнити вызывает их, когда перестраивает layout, то есть как раз, когда нам нужно.

После изменения нужных нам параметров, нужно попросить Unity заново построить layout (LayoutRebuilder.ForceRebuildLayoutImmediate), желательно в том же самом кадре, чтобы не было никаких миганий. Но при этом не уйти в рекурсию.

Полный код решения:
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

[Serializable]
public class CellSizeRule
{
[field: SerializeField] public int MinCount { get; private set; }
[field: SerializeField] public Vector2 CellSize { get; private set; }
}

[RequireComponent(typeof(GridLayoutGroup))]
[ExecuteAlways]
public class DynamicCellSizer : UIBehaviour, ILayoutController
{
[SerializeField] private CellSizeRule[] rules;

private GridLayoutGroup grid;
private int count = -1;

private GridLayoutGroup Grid
{
get
{
if (!grid)
grid = GetComponent<GridLayoutGroup>();
return grid;
}
}

protected override void OnEnable()
{
base.OnEnable();
ApplyRule();
}

private void ApplyRuleIfNeeded()
{
var count = transform.Cast<Transform>()
.Where(t => t.gameObject.activeSelf)
.Select(t => t.GetComponent<LayoutElement>())
.Count(le => le == null || !le.ignoreLayout);

if (count == this.count)
return;

this.count = count;

ApplyRule();
}

private void ApplyRule()
{
foreach (var rule in rules.OrderByDescending(r => r.MinCount))
{
if (count >= rule.MinCount)
{
SetSize(rule);
return;
}
}

if (rules.Length > 0)
SetSize(rules[^1]);
}

private void SetSize(CellSizeRule rule)
{
Grid.cellSize = rule.CellSize;
LayoutRebuilder.ForceRebuildLayoutImmediate(Grid.transform.parent as RectTransform);
}

public void SetLayoutHorizontal() => ApplyRuleIfNeeded();
public void SetLayoutVertical() => ApplyRuleIfNeeded();

#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
ApplyRule();
}
#endif
}

#ui
  • 👍 9
  • 🔥 8
  • ❤ 1
  • 🤯 1
More from @spellsingergames
  1. Oct 1, 2026Автор
  2. Aug 31, 2026Post #235
  3. Aug 25, 2026Прошли модерацию на Crazy Games. Basic Launch продлится до 15 сентября или пока не наберет…
  4. Aug 24, 2026А мы, тем временем, идём покорять #crazy_games Вчера ночью отправили билд на модерацию. Мо…
  5. Aug 24, 20262 недели нужно рекоммендеру Яндекс игр, чтобы как-то отреагировать на изменения метрик. AR…
  6. Aug 14, 2026Шестой эпизод подкаста ГЕЙМДЕВ ПОД ПИВОМ уже во вторник, в 22:30МСК! Хорошенечко отоспитес…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →