Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Swap from Horizontal to Vertical Layout Group?

Discussion in 'Scripting' started by Rinoaheart, Jul 25, 2017.

  1. Rinoaheart

    Rinoaheart

    Joined:
    Jul 18, 2017
    Posts:
    5
    Hi, I am trying to change a Layout Group with buttons from Horizontal to Vertical and vice versa, so the buttons are displayed horizontally when the screen orientation is Portrait, and vertically when it's Landscape.

    I also want to change the position of this Layout so it's different in each screen orientation.

    Is there any way I can do this?

    Thank you really much!
     
  2. RoMax92

    RoMax92

    Joined:
    May 29, 2017
    Posts:
    135
    The layout group horizontal and vertical can't be assigned on the same object, fortunatly there are some solutions for you:
    - Delete and create a new layout group each time the screen change rotation
    - Create 2 panels with the layout groups, and assign the buttons to each one every time with transform.SetParent()

    I suggest to you the second one
     
    MeawPipat and UnityChefty like this.
  3. RoMax92

    RoMax92

    Joined:
    May 29, 2017
    Posts:
    135
    I wrote you some code

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class ChangeLayout  : MonoBehaviour {
    7.  
    8.     public VerticalLayoutGroup verticalGroup;
    9.     public HorizontalLayoutGroup horizontalGroup;
    10.     public Button[] buttons;
    11.  
    12.     void ChangeVerticalLayout()
    13.     {
    14.         verticalGroup.gameObject.SetActive ( true );
    15.         horizontalGroup.gameObject.SetActive ( false );
    16.         foreach ( Button _button in buttons )
    17.         {
    18.             _button.transform.SetParent ( verticalGroup.gameObject.transform );
    19.         }
    20.     }
    21.  
    22.     void ChangeHorizontalLayout()
    23.     {
    24.         verticalGroup.gameObject.SetActive ( false );
    25.         horizontalGroup.gameObject.SetActive ( true );
    26.         foreach ( Button _button in buttons )
    27.         {
    28.             _button.transform.SetParent ( horizontalGroup.gameObject.transform );
    29.         }
    30.     }
    31.  
    32. }
     
    MeawPipat and Schneider21 like this.
  4. danielesuppo

    danielesuppo

    Joined:
    Oct 20, 2015
    Posts:
    332
    Hi, I've modified the GridLayout to automatically swap from horizontal-vertical mode (just with 2 panels for now).
    It work in Editor mode and there's a slide range to change the percent size of the 1st panel.
    When the width of the screen is less than the height it will turn in vertical mode.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. namespace UnityEngine.UI
    5. {
    6.     [AddComponentMenu("Layout/Grid Layout Group", 152)]
    7.     public class TwoPanelsLayout : LayoutGroup
    8.     {
    9.         public enum Corner { UpperLeft = 0, UpperRight = 1, LowerLeft = 2, LowerRight = 3 }
    10.         public enum Axis { Horizontal = 0, Vertical = 1 }
    11.         public enum Constraint { Flexible = 0, FixedColumnCount = 1, FixedRowCount = 2 }
    12.  
    13.         private protected Corner m_StartCorner = Corner.UpperLeft;
    14.         private Corner startCorner { get { return m_StartCorner; } set { SetProperty(ref m_StartCorner, value); } }
    15.  
    16.         private protected Axis m_StartAxis = Axis.Horizontal;
    17.         private Axis startAxis { get { return m_StartAxis; } set { SetProperty(ref m_StartAxis, value); } }
    18.  
    19.         private Vector2 m_CellSize = new Vector2(100, 100);
    20.         public Vector2 cellSize { get { return m_CellSize; } set { SetProperty(ref m_CellSize, value); } }
    21.  
    22.         private Vector2 m_Spacing = Vector2.zero;
    23.         public Vector2 spacing { get { return m_Spacing; } set { SetProperty(ref m_Spacing, value); } }
    24.  
    25.         private protected Constraint m_Constraint = Constraint.Flexible;
    26.         public Constraint constraint { get { return m_Constraint; } set { SetProperty(ref m_Constraint, value); } }
    27.  
    28.         private protected int m_ConstraintCount = 2;
    29.         public int constraintCount { get { return m_ConstraintCount; } set { SetProperty(ref m_ConstraintCount, Mathf.Max(1, value)); } }
    30.  
    31.         protected TwoPanelsLayout()
    32.         { }
    33.  
    34.         [Header ("Custom")]
    35.  
    36.         [Range (0,1)]
    37.         [SerializeField] protected float size = 0.5f;
    38.         private float _panelSizeDivision;
    39.  
    40.         [SerializeField] protected bool swapOnChangeAxis = true;
    41.         private Constraint _initConstrain;
    42.         private bool _isSwapped;
    43.  
    44.  
    45. #if UNITY_EDITOR
    46.         protected override void OnValidate()
    47.         {
    48.             base.OnValidate();
    49.             constraintCount = constraintCount;
    50.         }
    51. #endif
    52.  
    53.  
    54.  
    55.  
    56.         public override void CalculateLayoutInputHorizontal()
    57.         {
    58.             base.CalculateLayoutInputHorizontal();
    59.  
    60.             int minColumns = 0;
    61.             int preferredColumns = 0;
    62.             if (m_Constraint == Constraint.FixedColumnCount)
    63.             {
    64.                 minColumns = preferredColumns = m_ConstraintCount;
    65.             }
    66.             else if (m_Constraint == Constraint.FixedRowCount)
    67.             {
    68.                 minColumns = preferredColumns = Mathf.CeilToInt(rectChildren.Count / (float)m_ConstraintCount - 0.001f);
    69.             }
    70.             else
    71.             {
    72.                 minColumns = 1;
    73.                 preferredColumns = Mathf.CeilToInt(Mathf.Sqrt(rectChildren.Count));
    74.             }
    75.  
    76.             SetLayoutInputForAxis(
    77.                 padding.horizontal + (cellSize.x + spacing.x) * minColumns - spacing.x,
    78.                 padding.horizontal + (cellSize.x + spacing.x) * preferredColumns - spacing.x,
    79.                 -1, 0);
    80.         }
    81.  
    82.         public override void CalculateLayoutInputVertical()
    83.         {
    84.             int minRows = 0;
    85.             if (m_Constraint == Constraint.FixedColumnCount)
    86.             {
    87.                 minRows = Mathf.CeilToInt(rectChildren.Count / (float)m_ConstraintCount - 0.001f);
    88.             }
    89.             else if (m_Constraint == Constraint.FixedRowCount)
    90.             {
    91.                 minRows = m_ConstraintCount;
    92.             }
    93.             else
    94.             {
    95.                 float width = rectTransform.rect.size.x;
    96.                 int cellCountX = Mathf.Max(1, Mathf.FloorToInt((width - padding.horizontal + spacing.x + 0.001f) / (cellSize.x + spacing.x)));
    97.                 minRows = Mathf.CeilToInt(rectChildren.Count / (float)cellCountX);
    98.             }
    99.  
    100.             float minSpace = padding.vertical + (cellSize.y + spacing.y) * minRows - spacing.y;
    101.             SetLayoutInputForAxis(minSpace, minSpace, -1, 1);
    102.         }
    103.  
    104.         public override void SetLayoutHorizontal()
    105.         {
    106.             SetCellsAlongAxis(0);
    107.         }
    108.  
    109.         public override void SetLayoutVertical()
    110.         {
    111.             SetCellsAlongAxis(1);
    112.         }
    113.  
    114.         private void SetCellsAlongAxis(int axis)
    115.         {
    116.             // Normally a Layout Controller should only set horizontal values when invoked for the horizontal axis
    117.             // and only vertical values when invoked for the vertical axis.
    118.             // However, in this case we set both the horizontal and vertical position when invoked for the vertical axis.
    119.             // Since we only set the horizontal position and not the size, it shouldn't affect children's layout,
    120.             // and thus shouldn't break the rule that all horizontal layout must be calculated before all vertical layout.
    121.  
    122.  
    123.             RectTransform _rect = GetComponent<RectTransform>();
    124.             float screenWidth = _rect.sizeDelta.x;
    125.             float screenHeight = _rect.sizeDelta.y;
    126.  
    127.  
    128.             if (screenWidth > screenHeight)
    129.             {
    130.                 constraint = Constraint.FixedColumnCount;
    131.             }
    132.  
    133.             else if (screenWidth <= screenHeight)
    134.             {
    135.                 constraint = Constraint.FixedRowCount;
    136.             }
    137.  
    138.  
    139.  
    140.  
    141.  
    142.  
    143.  
    144.             if (axis == 0)
    145.             {
    146.                 // Only set the sizes when invoked for horizontal axis, not the positions.
    147.                 for (int i = 0; i < rectChildren.Count; i++)
    148.                 {
    149.                     RectTransform rect = rectChildren[i];
    150.  
    151.                     m_Tracker.Add(this, rect,
    152.                         DrivenTransformProperties.Anchors |
    153.                         DrivenTransformProperties.AnchoredPosition |
    154.                         DrivenTransformProperties.SizeDelta);
    155.  
    156.                     rect.anchorMin = Vector2.up;
    157.                     rect.anchorMax = Vector2.up;
    158.                     rect.sizeDelta = cellSize;
    159.                 }
    160.                 return;
    161.             }
    162.  
    163.             float width = rectTransform.rect.size.x;
    164.             float height = rectTransform.rect.size.y;
    165.  
    166.             int cellCountX = 1;
    167.             int cellCountY = 1;
    168.             if (m_Constraint == Constraint.FixedColumnCount)
    169.             {
    170.                 cellCountX = m_ConstraintCount;
    171.                 cellCountY = Mathf.CeilToInt(rectChildren.Count / (float)cellCountX - 0.001f);
    172.             }
    173.             else if (m_Constraint == Constraint.FixedRowCount)
    174.             {
    175.                 cellCountY = m_ConstraintCount;
    176.                 cellCountX = Mathf.CeilToInt(rectChildren.Count / (float)cellCountY - 0.001f);
    177.             }
    178.             else
    179.             {
    180.                 if (cellSize.x + spacing.x <= 0)
    181.                     cellCountX = int.MaxValue;
    182.                 else
    183.                     cellCountX = Mathf.Max(1, Mathf.FloorToInt((width - padding.horizontal + spacing.x + 0.001f) / (cellSize.x + spacing.x)));
    184.  
    185.                 if (cellSize.y + spacing.y <= 0)
    186.                     cellCountY = int.MaxValue;
    187.                 else
    188.                     cellCountY = Mathf.Max(1, Mathf.FloorToInt((height - padding.vertical + spacing.y + 0.001f) / (cellSize.y + spacing.y)));
    189.             }
    190.  
    191.             int cornerX = (int)startCorner % 2;
    192.             int cornerY = (int)startCorner / 2;
    193.  
    194.             int cellsPerMainAxis, actualCellCountX, actualCellCountY;
    195.             if (startAxis == Axis.Horizontal)
    196.             {
    197.                 cellsPerMainAxis = cellCountX;
    198.                 actualCellCountX = Mathf.Clamp(cellCountX, 1, rectChildren.Count);
    199.                 actualCellCountY = Mathf.Clamp(cellCountY, 1, Mathf.CeilToInt(rectChildren.Count / (float)cellsPerMainAxis));
    200.             }
    201.             else
    202.             {
    203.                 cellsPerMainAxis = cellCountY;
    204.                 actualCellCountY = Mathf.Clamp(cellCountY, 1, rectChildren.Count);
    205.                 actualCellCountX = Mathf.Clamp(cellCountX, 1, Mathf.CeilToInt(rectChildren.Count / (float)cellsPerMainAxis));
    206.             }
    207.  
    208.             Vector2 requiredSpace = new Vector2(
    209.                     actualCellCountX * cellSize.x + (actualCellCountX - 1) * spacing.x,
    210.                     actualCellCountY * cellSize.y + (actualCellCountY - 1) * spacing.y
    211.                     );
    212.             Vector2 startOffset = new Vector2(
    213.                     GetStartOffset(0, requiredSpace.x),
    214.                     GetStartOffset(1, requiredSpace.y)
    215.                     );
    216.  
    217.  
    218.  
    219.             //Swap panels
    220.             if (swapOnChangeAxis && constraint != _initConstrain)
    221.             {
    222.                 rectChildren[1].SetSiblingIndex(0);
    223.                 _initConstrain = constraint;
    224.                 _isSwapped = !_isSwapped;
    225.                 //Debug.Log("SWAPPED");
    226.             }
    227.  
    228.  
    229.  
    230.             if (!_isSwapped) _panelSizeDivision = size;
    231.             else _panelSizeDivision = 1 - size;
    232.  
    233.  
    234.  
    235.  
    236.             for (int i = 0; i < rectChildren.Count; i++)
    237.             {
    238.                 int positionX;
    239.                 int positionY;
    240.                 if (startAxis == Axis.Horizontal)
    241.                 {
    242.                     positionX = i % cellsPerMainAxis;
    243.                     positionY = i / cellsPerMainAxis;
    244.                 }
    245.                 else
    246.                 {
    247.                     positionX = i / cellsPerMainAxis;
    248.                     positionY = i % cellsPerMainAxis;
    249.                 }
    250.  
    251.                 if (cornerX == 1)
    252.                     positionX = actualCellCountX - 1 - positionX;
    253.                 if (cornerY == 1)
    254.                     positionY = actualCellCountY - 1 - positionY;
    255.  
    256.              
    257.  
    258.                 float multiplier = 0;
    259.                 float factorX = 0;
    260.                 float factorY = 0;
    261.  
    262.                 if (i == 0) multiplier = (1 - _panelSizeDivision) * 2;
    263.                 if (i == 1) multiplier = 2 - ((1 - _panelSizeDivision) * 2);
    264.  
    265.  
    266.                 float realsizeX = 0;
    267.  
    268.                 if (m_Constraint == Constraint.FixedColumnCount)
    269.                 {
    270.                     realsizeX = ((width - (spacing[0] * (actualCellCountX - 1))) / actualCellCountX) * multiplier;
    271.                     factorX = (realsizeX / multiplier) - (realsizeX - (realsizeX / multiplier));
    272.                 }
    273.                  
    274.                 else if (m_Constraint == Constraint.FixedRowCount)
    275.                 {
    276.                     realsizeX = ((width - (spacing[0] * (actualCellCountX - 1))) / actualCellCountX);
    277.                     factorX = realsizeX;
    278.                 }
    279.                  
    280.  
    281.                 SetChildAlongAxis(rectChildren[i], 0, startOffset.x + (factorX + spacing[0]) * positionX , realsizeX);
    282.  
    283.  
    284.  
    285.                 float realsizeY = 0;
    286.  
    287.                 if (m_Constraint == Constraint.FixedRowCount)
    288.                 {
    289.                     realsizeY = ((height - (spacing[0] * (actualCellCountY - 1))) / actualCellCountY) * multiplier;
    290.                     factorY = (realsizeY / multiplier) - (realsizeY - (realsizeY / multiplier));
    291.                 }
    292.                  
    293.                 else if (m_Constraint == Constraint.FixedColumnCount)
    294.                 {
    295.                     realsizeY = ((height - (spacing[0] * (actualCellCountY - 1))) / actualCellCountY);
    296.                     factorY = realsizeY;
    297.                 }
    298.                  
    299.  
    300.                 SetChildAlongAxis(rectChildren[i], 1, startOffset.y + (factorY + spacing[1]) * positionY, realsizeY);
    301.             }
    302.         }
    303.     }
    304. }
     
  5. lemartialou

    lemartialou

    Joined:
    Jul 10, 2015
    Posts:
    16
    Hi, so I also wanted to be able to switch between horizontal and vertical layout on the fly, from the same component.

    Fortunately I realized that HorizontalLayoutGroup and VerticalLayoutGroup are pretty thin layers over the HorizontalOrVerticalGroup script, they both have the same code with just a simple variable changed.

    So I combined them into a simple script, and now I can switch between horizontal and vertical on the fly, from the inspector or from script (via the "isVertical" bool property).

    Hope you'll like it ! :)

    Edit: Fixed some typos.



    Code (CSharp):
    1. #if UNITY_EDITOR
    2.  
    3. using UnityEngine.UI;
    4.  
    5. #endif
    6.  
    7. // This is a simple class that combines HorizontalLayoutGroup and VerticalLayoutGroup,
    8. // so we can switch between horizontal and vertical layout easily.
    9.  
    10. // From script we can use the "isVertical" bool property to control the axis.
    11. // From inspector this bool is controlled by a more convenient "Layout Axis" enum.
    12.  
    13. namespace UnityEngine.UI
    14. {
    15.     [AddComponentMenu("Layout/HV Layout Group", 153)]
    16.     public class HVLayoutGroup : HorizontalOrVerticalLayoutGroup
    17.     {
    18.         protected HVLayoutGroup() { }
    19.  
    20.         [SerializeField] protected bool m_IsVertical = true;
    21.         public bool isVertical { get { return m_IsVertical; } set { SetProperty(ref m_IsVertical, value); } }
    22.  
    23.         public override void CalculateLayoutInputHorizontal()
    24.         {
    25.             base.CalculateLayoutInputHorizontal();
    26.             CalcAlongAxis(0, m_IsVertical);
    27.         }
    28.  
    29.         public override void CalculateLayoutInputVertical()
    30.         {
    31.             CalcAlongAxis(1, m_IsVertical);
    32.         }
    33.  
    34.         public override void SetLayoutHorizontal() => SetChildrenAlongAxis(0, m_IsVertical);
    35.         public override void SetLayoutVertical() => SetChildrenAlongAxis(1, m_IsVertical);
    36.     }
    37. }
    38.  
    39. #if UNITY_EDITOR
    40.  
    41. namespace UnityEditor.UI
    42. {
    43.     [CustomEditor(typeof(HVLayoutGroup), true)]
    44.     [CanEditMultipleObjects]
    45.     public class HVLayoutGroupEditor : HorizontalOrVerticalLayoutGroupEditor
    46.     {
    47.         private enum LayoutAxis { Horizontal, Vertical }
    48.  
    49.         private LayoutAxis m_LayoutAxis;
    50.         private SerializedProperty m_IsVertical;
    51.  
    52.         protected override void OnEnable()
    53.         {
    54.             base.OnEnable();
    55.             m_IsVertical = serializedObject.FindProperty("m_IsVertical");
    56.         }
    57.  
    58.         public override void OnInspectorGUI()
    59.         {
    60.             serializedObject.Update();
    61.  
    62.             //EditorGUILayout.PropertyField(m_IsVertical, true);
    63.  
    64.             m_LayoutAxis = m_IsVertical.boolValue ? LayoutAxis.Vertical : LayoutAxis.Horizontal;
    65.             m_LayoutAxis = (LayoutAxis)EditorGUILayout.EnumPopup("Layout Axis", m_LayoutAxis);
    66.             m_IsVertical.boolValue = m_LayoutAxis == LayoutAxis.Vertical ? true : false;
    67.  
    68.             serializedObject.ApplyModifiedProperties();
    69.  
    70.             base.OnInspectorGUI();
    71.         }
    72.     }
    73. }
    74.  
    75. #endif
     

    Attached Files:

    Last edited: Jun 23, 2022