I want a layout controller capable of expanding its height based on its children. Basically like a text area works, but instead of words there would be child elements.
So when adding elements (of variable width) to this layout controller, it would first try to fill its width before going to a new row.
Is there currently a way to set this up using the pre-existing components in Unity UI? The horizontal and vertical layout groups don’t handle rows or columns, respectively. The grid layout seems to require a fixed size of each cell, which is undesirable.
Obviously a combination of vertical and horizontal groups can be used, but it would be far neater if there’s a way to set up a grid layout capable of having children of flexible width. Otherwise a tree of vertical and horizontal groups would have to be rebuilt when children at the top are removed.
Either I’m misunderstanding you, or you me. How would creating a content size fitter automatically create rows, or columns, for a layout group? As far as I know, grid layout is the only layout creating both columns and rows, but that requires a fixed cell size.
Yeah easy enough (and easy to say!), but you’ll have to write this yourself. The UI is open sourced and the UI components are all standard C# components, so a good starting point would be to extend or do a custom component based on GridLayoutGroup.
No the current Grid layout won’t dynamically change it’s row value. (just answered that in another post, second today, v.odd)
Will see if the community comes up with anything or I’ll add one myself to the UI Extensions repo https://bitbucket.org/ddreaper/unity-ui-extensions
So, it wont resize dynamically. What I did was on Start() get the width of the scrolling area (the parent) and then divide it by the number of columns I wanted then changed the fixed cell size in the Grid Layout Group.
Here is my code:
public float width;
// Use this for initialization
void Start ()
{
width = this.gameObject.GetComponent<RectTransform>().rect.width;
Vector2 newSize = new Vector2(width / 2, width / 2);
this.gameObject.GetComponent<GridLayoutGroup>().cellSize = newSize;
}
You can also do the same for height. Or put the code in Update() if you want it to resize constantly.
I know this isn’t the OP’s desire but in case anyone finds themselves here (like I did) looking for solution to maximising the cell size in a GridLayoutGroup, I extended the code snippet above from @jamesburford - HTH
using System;
using UGS.unityutils.attributes;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UI;
namespace ui {
/// <summary>
/// Configures a <see cref="GridLayoutGroup.cellSize"/> on same GameObject to have
/// maximum size for the specified number of rows or columns.
/// By default, it uses the number from the constraint configured in the <see cref="GridLayoutGroup"/>.
/// Use the 'override' values here to override or specify a size
/// when <see cref="GridLayoutGroup.Constraint.Flexible"/> used.
/// </summary>
public sealed class GridLayoutMaximiser : MonoBehaviour {
[Tooltip("Override the number of columns to aim for (or zero for default/disabled).\n" +
"Takes priority over rows.")]
[SerializeField]
private int numColumnsOverride = 0;
[Tooltip("Override the number of rows to aim for (or zero for default/disabled).")]
[SerializeField]
private int numRowsOverride = 0;
public void OnValidate() {
Assert.IsNotNull(transform as RectTransform);
Assert.IsNotNull(GetComponent<GridLayoutGroup>());
}
public void OnEnable() {
setSizes();
}
[ContextMenu("Set sizes")]
private void setSizes() {
var gridLayoutGroup = GetComponent<GridLayoutGroup>();
var columns = this.numColumnsOverride;
var rows = this.numRowsOverride;
switch (gridLayoutGroup.constraint) {
case GridLayoutGroup.Constraint.Flexible: // nop
break;
case GridLayoutGroup.Constraint.FixedColumnCount:
columns = gridLayoutGroup.constraintCount;
break;
case GridLayoutGroup.Constraint.FixedRowCount:
rows = gridLayoutGroup.constraintCount;
break;
default:
throw new ArgumentOutOfRangeException(gridLayoutGroup.constraint.ToString());
}
var padding = gridLayoutGroup.padding;
var spacing = gridLayoutGroup.spacing;
var size = ((RectTransform) transform).rect.size - new Vector2(padding.horizontal, padding.vertical);
float width, height;
if (0 < columns) {
width = (size.x - (columns - 1) * spacing.x) / columns;
if (0 < rows) {
height = (size.y - (rows - 1) * spacing.y) / rows;
} else {
// TODO: account for different vertical to horizontal spacing
height = width;
}
} else {
if (0 < rows) { // rows specified but not columns
// TODO: account for different vertical to horizontal spacing
width = height = (size.y - (rows - 1) * spacing.y) / rows;
} else { // neither specified
return;
}
}
gridLayoutGroup.cellSize = new Vector2(width, height);
}
}
}
In case someone was trying to make a ‘flexible’ UI like me and ended up here as well, I went with @lkc2015 logic with some changes. No code needed.
I wanted to split the grid in 4 equal cells, and when the parent canvas is resized (in different aspect ratios), the cells are also resized automatically, preserving their aspect ratio, and without overlapping each other.
Here is the hierarchy of objects:
Root Canvas (Screen Space - Camera)
– Flexible Canvas (Vertical Layout Group, with Child Control Size checked in both width and height)
---- Row 1 Canvas (Horizontal Layout Group, with Child Control Size checked in both width and height)
------ Child Canvas 1
------ Child Canvas 2
---- Row 2 Canvas (Same as Row 1)
------ Child Canvas 3
------ Child Canvas 4
Here is the result
Now you can put your own UI objects inside each Canvas and they will resize with it. (You might need to add an Aspect Ratio Fitter to your buttons and UI elements though and set their Rect Transform to Stretch).
Is there a better way ?