Last active
March 30, 2021 09:45
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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