I have a horizontal ScrollRect that contains a panel with a GridLayoutGroup and ContentSize fitter with horizontal fit as Preferred Size and vertical fit as Unconstrained. I noticed that sometimes the scroll bar would let me scroll even when the objects fit on the panel just fine and sometime wouldn’t let me scroll to the end.
When I change the Vertical Fit to Preferred Size the panel seems to start matching what I’m actually seeing happening with the scroll panel, so it seems that the Content Size Fitter is using Preferred Size instead of Unconstrained fit.
Here’s a picture, as you can see the green child panels go beyond the red panel with the content size fitter:
First, this is what happens with the flexible GridLayoutGroup and ContentSizeFitter with horizontal fit as preferred and vertical fit as unconstrained:
Note that the scroll bar didn’t change at all but now matches the panel size. The panel’s width didn’t change, but the height did, so it seems it always calculates the “preferred size” width as if the vertical fit was “preferred size” too.
Not fixed in 4.6.1p3. I created a new bug report with a new repro project, ticket number 662142. I would really appreciate some kind of reply from the Unity folks.
Just… wow. That’s some seriously horrible design then. I’d think it’s not too much ask to have a layout with a dynamical row count based on available space and horizontal stretching.
While testing a workaround I noticed that the Flexible GridLayoutGroup + ContentSizeFitter with Unconstrained + PreferredSize fitting already works vertically, just not horizontally! I can’t believe that’s by design and not a bug.
Anyway, here’s my workaround, if someone needs it. It dynamically sets the GridLayoutGroup rows based on the RectTransform size. Add the script to a RectTransform that has a GridLayoutGroup and a ContentSizeFitter component.
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
[RequireComponent(typeof(GridLayoutGroup))]
[RequireComponent(typeof(ContentSizeFitter))]
[RequireComponent(typeof(RectTransform))]
public class DynamicGridSize : UIBehaviour
{
private GridLayoutGroup grid;
private ContentSizeFitter fitter;
bool initialized = false;
protected override void Awake()
{
base.Awake();
grid = GetComponent<GridLayoutGroup>();
fitter = GetComponent<ContentSizeFitter>();
initialized = true;
}
protected override void Start()
{
base.Start();
CalculateGridSize();
}
protected override void OnRectTransformDimensionsChange()
{
base.OnRectTransformDimensionsChange();
CalculateGridSize();
}
public void CalculateGridSize()
{
// Seems OnRectTransformDimensionsChange gets called before Awake
if (!initialized)
{
return;
}
RectTransform rectTrans = (RectTransform)transform;
// Strech vertically
if (fitter.verticalFit == ContentSizeFitter.FitMode.PreferredSize && fitter.horizontalFit == ContentSizeFitter.FitMode.Unconstrained)
{
int columns = (int)(rectTrans.rect.width / (grid.cellSize.x + grid.spacing.x));
grid.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
grid.constraintCount = columns;
}
// Stretch horizontally
else if (fitter.verticalFit == ContentSizeFitter.FitMode.Unconstrained && fitter.horizontalFit == ContentSizeFitter.FitMode.PreferredSize)
{
int rows = (int)(rectTrans.rect.height / (grid.cellSize.y + grid.spacing.y));
grid.constraint = GridLayoutGroup.Constraint.FixedRowCount;
grid.constraintCount = rows;
}
else
{
// Nothing to do
return;
}
}
}
Hence the GridLayoutGroup can’t know calculate it’s width based on its height unless you give it extra information that lets it know its height even before the width and heights have been calculated. This is what the Constarint property is for.
I’m glad you found a solution that works for you.
I’m you’re wondering why we don’t do this in the built-in components, it’s because it creates ambiguity about whether the RectTranform height of the grid is an input or an output. Say you had the ContentSizeFitter height set to “preferred height”. Then, with your script, the height would both be read as input for the layout system and then the result would also set the height, potentially overriding your manually set height value with a different one. Sometimes this can turn out to work fine; other times it can create weird positive or negative feedback loops that can manifest as weird bugs where the grid might keep on growing by itself, or maybe oscillating in size.
Alternatively, the RectTransform should look at what is setup in the ContentSizeFitter in order to determine if it should set its row count based on the height or not, that would be a hack. ContentSizeFitter might be placed on a parent component, or the user might have created a different component that is similar to ContentSizeFitter but a bit different. So we can’t have a hard-coded check for specific ContentSizeFitter settings inside the GridLayoutGroup class.
What we can do is provide the Constraint property on the Grid Layout Group.[/QUOTE]