Dynamic Unity UI Layout Groups

Posted by Robert C. Wahler on Wed, Nov 7, 2018
In Development
Tags unity, patterns, ui

Give your layout groups some smarts. This DynamicLayoutGroup group switches orientation automatically between landscape and portrait layouts. It acts like a VerticalLayoutGroup (screenshots below) or a HorizontalLayoutGroup depending the current aspect ratio of the screen.

Zen Unity Vertical

The Why

These screenshots are from an upcoming 2.0 update to Fourtex Zen. The game shipped with a fixed landscape orientation. We recently moved to Unity 5 and decided that updating Fourtex Zen would be a good way to learn the new UI system. We wanted the game playable regardless of screen orientation. The poem panels stack the poetry text in portrait and place it side-by-side in landscape (there are 64 poems given as in-game rewards).

Zen Unity Horizontal

The Code

The DynamicLayoutGroup class inherits directly from the Unity 5 HorizontalOrVerticalLayoutGroup base class and provides a simple mechanism to determine screen orientation and shift its layout accordingly. It can be used anywhere you would normally use a VerticalLayoutGroup or HorizontalLayoutGroup.

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
using System.Collections.Generic;

namespace SDD.UI {

  /// <summary>
  /// Layout group switches orientation automatically between landscape and
  /// portrait layouts so it either acts like a VerticalLayoutGroup or a
  /// HorizontalLayoutGroup.
  /// </summary>
  [AddComponentMenu("Layout/Dynamic Layout Group", 150)]
  public class DynamicLayoutGroup : HorizontalOrVerticalLayoutGroup {

    /// <summary>
    /// When is the layout vertical? In portrait or landscape?
    /// </summary>
    [SerializeField]
    public ScreenOrientation verticalWhen = ScreenOrientation.Portrait;

    public bool IsVertical { get { return GetIsVertical(); }}

    private bool GetIsVertical() {
      bool isVertical;

      if (UnityEngine.Screen.width > UnityEngine.Screen.height) {
        //orientation = ScreenOrientation.Landscape;
        isVertical = (verticalWhen == ScreenOrientation.Landscape) ? true : false;
      }
      else {
        //orientation = ScreenOrientation.Portrait;
        isVertical = (verticalWhen == ScreenOrientation.Portrait) ? true : false;
      }

      //Log.Debug(string.Format("DynamicLayoutGroup.OnRectTransformDimensionsChange() isVertical={0}a, ID={1}", isVertical, GetInstanceID()));
      return isVertical;
    }

    public override void CalculateLayoutInputHorizontal() {
      //Log.Debug(string.Format("DynamicLayoutGroup.CalculateLayoutInputHorizontal() IsVertical={0}, ID={1}", IsVertical, GetInstanceID()));

      base.CalculateLayoutInputHorizontal();
      CalcAlongAxis(0, isVertical: IsVertical);
    }

    public override void CalculateLayoutInputVertical() {
      //Log.Debug(string.Format("DynamicLayoutGroup.CalculateLayoutInputVertical() IsVertical={0}, ID={1}", IsVertical, GetInstanceID()));

      CalcAlongAxis(1, isVertical: IsVertical);
    }

    public override void SetLayoutHorizontal() {
      //Log.Debug(string.Format("DynamicLayoutGroup.SetLayoutHorizontal() IsVertical={0}, ID={1}", IsVertical, GetInstanceID()));

      SetChildrenAlongAxis(0, isVertical: IsVertical);
    }

    public override void SetLayoutVertical() {
      //Log.Debug(string.Format("DynamicLayoutGroup.SetLayoutVertical() IsVertical={0}, ID={1}", IsVertical, GetInstanceID()));

      SetChildrenAlongAxis(1, isVertical: IsVertical);
    }
  }
}
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

namespace SDD.UI {

  /// <summary>
  /// Override the Unity UI custom editor for HorizontalOrVerticalLayoutGroup
  /// and do exactly nothing. The only purpose of this class is to expose the
  /// public ```VerticalWhen```. The inherited editor prevents editing new
  /// publics in descendant classes without doing a full widget layout here
  /// (too much work!).
  /// </summary>
  /// <remarks>
  /// Place in ```Editor``` folder
  /// </remarks>
  [CustomEditor(typeof(SDD.UI.DynamicLayoutGroup), true)]
  [CanEditMultipleObjects]
  public class DynamicLayoutGroupEditor : UnityEditor.Editor {
  }
}
namespace SDD {

  /// <summary>
  /// Orientation of the screen regardless of which way is up
  /// </summary>
  public enum ScreenOrientation {
    Landscape,
    Portrait
  }

GitHub Gist

This article originally appeared on SaltyDog.digital