Skip to content

Instantly share code, notes, and snippets.

@TobiasPott
Last active March 30, 2021 09:45
Show Gist options
  • Save TobiasPott/e4c2f3383ebc28e9a7c80a5cd8a7633e to your computer and use it in GitHub Desktop.
Save TobiasPott/e4c2f3383ebc28e9a7c80a5cd8a7633e to your computer and use it in GitHub Desktop.
A simple auto fit component extending the GridLayoutGroup to automatically fit all children inside of it based on the grid layout orientation.
using UnityEngine;
using UnityEngine.UI;
namespace NoXP
{
[ExecuteAlways()]
[RequireComponent(typeof(GridLayoutGroup))]
public class AutoFitGridLayout : MonoBehaviour
{
private RectTransform _rectTransform = null;
protected RectTransform RectTransform
{
get
{
if (_rectTransform == null)
this._rectTransform = this.transform as RectTransform;
return _rectTransform;
}
}
private GridLayoutGroup _gridLayout = null;
protected GridLayoutGroup GridLayout
{
get
{
if (_gridLayout == null)
this._gridLayout = this.gameObject.GetComponent<GridLayoutGroup>();
return _gridLayout;
}
}
protected void OnRectTransformDimensionsChange()
{
this.AutoFit();
}
private void OnTransformChildrenChanged()
{
this.AutoFit();
}
protected void OnValidate()
{
this.AutoFit();
}
private void AutoFit() => this.AutoFit(this.GridLayout.startAxis);
private void AutoFit(GridLayoutGroup.Axis axis)
{
this.GridLayout.constraint = GridLayoutGroup.Constraint.Flexible;
this.GridLayout.startAxis = axis;
int childCount = GetActiveChildCount(this.transform);
if (axis == GridLayoutGroup.Axis.Horizontal)
this.AutoFitInHorizontalRow(childCount);
else if (axis == GridLayoutGroup.Axis.Vertical)
this.AutoFitInVerticlaRow(childCount);
}
/// <summary>
/// Calculates the horizontal auto-fit values for the given number of elements
/// </summary>
/// <param name="elementCount">Number of elements to calculate auto-fit sizes for</param>
/// <param name="rowCount">Number of rows the elements get distributed over (this parameter is not functional at the moment)</param>
private void AutoFitInHorizontalRow(int elementCount, int rowCount = 1)
{
Vector2 size = this.RectTransform.rect.size;
// subtract padding
size.x -= this.GridLayout.padding.horizontal;
size.y -= this.GridLayout.padding.vertical;
// subtract spacing
size.x -= this.GridLayout.spacing.x * (elementCount - 1);
size.x -= this.GridLayout.spacing.y * (rowCount - 1);
float cellHeight = size.y;
float cellWidth = size.x / elementCount;
float cellSize = Mathf.Min(cellHeight, cellWidth);
GridLayout.cellSize = new Vector2(cellSize, cellSize);
}
/// <summary>
/// Calculates the vertical auto-fit values for the given number of elements
/// </summary>
/// <param name="elementCount">Number of elements to calculate auto-fit sizes for</param>
/// <param name="colCount">Number of columns the elements get distributed over (this parameter is not functional at the moment)</param>
private void AutoFitInVerticlaRow(int elementCount, int colCount = 1)
{
Vector2 size = this.RectTransform.rect.size;
// subtract padding
size.x -= this.GridLayout.padding.horizontal;
size.y -= this.GridLayout.padding.vertical;
// subtract spacing
size.x -= this.GridLayout.spacing.x * (colCount - 1);
size.x -= this.GridLayout.spacing.y * (elementCount - 1);
float cellHeight = size.y / elementCount;
float cellWidth = size.x;
float cellSize = Mathf.Min(cellHeight, cellWidth);
GridLayout.cellSize = new Vector2(cellSize, cellSize);
}
private static int GetActiveChildCount(Transform parent)
{
// early exit check against null
if (parent == null)
return 0;
// count active child objects
int count = 0;
for (int i = 0; i < parent.childCount; i++)
if (parent.GetChild(i).gameObject.activeSelf) count++;
return count;
}
// in-editor auto updates
#if UNITY_EDITOR
private GridLayoutGroup.Axis _startAxis = GridLayoutGroup.Axis.Horizontal;
private Vector2 _spacing = new Vector2(0, 0);
private Vector4 _padding = Vector4.zero;
private int _activeChildrenCount = 0;
private void Update()
{
bool refresh = false;
RectOffset gridPadding = this.GridLayout.padding;
if (!Mathf.Approximately(gridPadding.left, _padding.x)
|| !Mathf.Approximately(gridPadding.top, _padding.y)
|| !Mathf.Approximately(gridPadding.right, _padding.z)
|| !Mathf.Approximately(gridPadding.bottom, _padding.w))
refresh = true;
if (this.GridLayout.startAxis != _startAxis)
refresh = true;
if (this.GridLayout.spacing != _spacing)
refresh = true;
if (_activeChildrenCount != GetActiveChildCount(this.transform))
refresh = true;
// refresh child auto fit if required
if (refresh)
this.AutoFit();
// update cache fields
_padding.x = gridPadding.left;
_padding.y = gridPadding.top;
_padding.z = gridPadding.right;
_padding.w = gridPadding.bottom;
_startAxis = this.GridLayout.startAxis;
_spacing = this.GridLayout.spacing;
_activeChildrenCount = GetActiveChildCount(this.transform);
}
#endif
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment