2025-06-17 20:19:06 +09:00
using System ;
using UnityEngine ;
using UnityEngine.UI ;
namespace UVC.Extension
{
public static class LayoutGroupEx
{
/// <summary>
/// LayoutGroup의 직접 자식 요소들의 정렬을 시도합니다.
/// 각 자식의 앵커와 피벗은 (0.5, 0.5)로 설정되며, localPosition이 조정됩니다.
/// </summary>
/// <param name="layoutGroup">정렬할 LayoutGroup입니다.</param>
/// <param name="alignment">적용할 정렬 방식입니다. 현재 MiddleLeft, MiddleCenter, MiddleRight만 특정 동작을 수행합니다.</param>
/// <remarks>
/// [초보자 설명]
/// 이 메서드는 LayoutGroup의 직접 자식들의 위치를 수동으로 조정합니다.
/// 각 자식의 앵커(anchorMin, anchorMax)와 피벗(pivot)은 모두 (0.5, 0.5)로 설정됩니다.
/// 이는 자식의 위치와 크기 기준점이 중앙으로 설정된다는 의미입니다.
///
/// 중요: 이 메서드는 HorizontalLayoutGroup, VerticalLayoutGroup, GridLayoutGroup과 같은
/// 활성 레이아웃 컴포넌트의 자동 레이아웃 계산과 충돌할 수 있습니다.
/// 레이아웃 컴포넌트가 활성화되어 있다면, 이 메서드에 의한 변경 사항이 다음 레이아웃 업데이트 시 덮어쓰여질 수 있습니다.
/// 이 메서드는 레이아웃 컴포넌트가 비활성화되었거나, 특정 상황에서 수동 제어가 필요할 때 제한적으로 사용해야 합니다.
///
/// 지원되는 'alignment' 값에 따른 동작:
/// - MiddleLeft: 자식의 왼쪽 가장자리가 부모(LayoutGroup)의 가로 중앙선에 정렬됩니다.
/// - MiddleCenter: 자식의 중앙이 부모의 중앙에 정렬됩니다.
/// - MiddleRight: 자식의 오른쪽 가장자리가 부모의 가로 중앙선에 정렬됩니다.
/// 다른 TextAnchor 값은 현재 특별한 동작을 수행하지 않거나 경고를 출력할 수 있습니다.
/// </remarks>
/// <example>
/// <code>
/// using UnityEngine;
/// using UnityEngine.UI;
/// using UVC.Extension; // LayoutGroupEx가 포함된 네임스페이스
///
/// public class LayoutAlignmentExample : MonoBehaviour
/// {
/// public LayoutGroup myLayoutGroup; // 인스펙터에서 할당
///
/// void Start()
/// {
/// if (myLayoutGroup != null)
/// {
/// // 예시: 모든 직접 자식들을 중앙 정렬 시도
/// // 주의: myLayoutGroup에 활성 Horizontal/Vertical/Grid LayoutGroup이 있다면 충돌 가능
/// myLayoutGroup.AlignChildren(TextAnchor.MiddleCenter);
/// }
/// }
/// }
/// </code>
/// </example>
public static void AlignChildren ( this UnityEngine . UI . LayoutGroup layoutGroup , UnityEngine . TextAnchor alignment )
{
if ( layoutGroup = = null ) throw new ArgumentNullException ( nameof ( layoutGroup ) ) ;
for ( int i = 0 ; i < layoutGroup . transform . childCount ; i + + )
{
RectTransform rect = layoutGroup . transform . GetChild ( i ) as RectTransform ;
if ( rect = = null ) continue ;
rect . anchorMin = new UnityEngine . Vector2 ( 0.5f , 0.5f ) ;
rect . anchorMax = new UnityEngine . Vector2 ( 0.5f , 0.5f ) ;
rect . pivot = new UnityEngine . Vector2 ( 0.5f , 0.5f ) ;
switch ( alignment )
{
case UnityEngine . TextAnchor . MiddleLeft :
rect . localPosition = new UnityEngine . Vector3 ( - rect . rect . width / 2 , 0 , 0 ) ;
break ;
case UnityEngine . TextAnchor . MiddleCenter :
rect . localPosition = UnityEngine . Vector3 . zero ;
break ;
case UnityEngine . TextAnchor . MiddleRight :
rect . localPosition = new UnityEngine . Vector3 ( rect . rect . width / 2 , 0 , 0 ) ;
break ;
default :
Debug . LogWarning ( $"LayoutGroupEx.AlignChildren: Alignment '{alignment}' is not explicitly supported or might not produce expected results with the current implementation. Only MiddleLeft, MiddleCenter, MiddleRight have defined behaviors." ) ;
break ;
}
}
// 변경 사항을 즉시 반영하려면 레이아웃 리빌드가 필요할 수 있습니다.
// 하지만 활성 레이아웃 그룹이 있다면 이 변경을 무시할 수 있습니다.
// if (layoutGroup.GetComponent<RectTransform>() != null)
// {
// LayoutRebuilder.ForceRebuildLayoutImmediate(layoutGroup.GetComponent<RectTransform>());
// }
}
/// <summary>
/// LayoutGroup의 모든 직접 자식 요소에 대해 LayoutElement의 flexibleWidth와 flexibleHeight를 설정하여 강제로 확장/축소하도록 합니다.
/// </summary>
/// <param name="layoutGroup">설정을 적용할 LayoutGroup입니다.</param>
/// <param name="expand">true로 설정하면 자식 요소가 사용 가능한 공간을 채우도록 확장되고 (flexible = 1), false면 축소됩니다 (flexible = 0).</param>
/// <remarks>
/// [초보자 설명]
/// LayoutElement 컴포넌트는 UI 요소가 LayoutGroup 내에서 어떻게 크기를 조절하고 배치될지 상세하게 제어합니다.
/// 'flexibleWidth' 또는 'flexibleHeight' 속성이 0보다 큰 값(보통 1)으로 설정되면, 해당 UI 요소는 LayoutGroup 내에서
/// 다른 'flexible' 요소들과 함께 사용 가능한 여유 공간을 나눠 가집니다. 'expand'를 true로 하면 이 값을 1로, false로 하면 0으로 설정합니다.
/// 또한, 'expand'가 true일 때 'preferredWidth'와 'preferredHeight'를 -1로 설정하여 LayoutElement가 크기를 유연하게 조절하도록 합니다.
/// 이 메서드는 각 직접 자식 GameObject에 LayoutElement 컴포넌트가 이미 존재한다고 가정합니다. 없다면 해당 자식은 영향을 받지 않습니다.
/// </remarks>
/// <example>
/// <code>
/// using UnityEngine;
/// using UnityEngine.UI;
/// using UVC.Extension;
///
/// public class LayoutExpandExample : MonoBehaviour
/// {
/// public VerticalLayoutGroup myLayoutGroup;
///
/// void Start()
/// {
/// if (myLayoutGroup != null)
/// {
/// // 모든 자식들이 세로 및 가로로 확장되도록 설정
/// myLayoutGroup.SetChildForceExpand(true);
/// }
/// }
/// }
/// </code>
/// </example>
public static void SetChildForceExpand ( this UnityEngine . UI . LayoutGroup layoutGroup , bool expand )
{
if ( layoutGroup = = null ) throw new ArgumentNullException ( nameof ( layoutGroup ) ) ;
for ( int i = 0 ; i < layoutGroup . transform . childCount ; i + + )
{
Transform child = layoutGroup . transform . GetChild ( i ) ;
LayoutElement element = child . GetComponent < LayoutElement > ( ) ;
if ( element ! = null )
{
element . flexibleWidth = expand ? 1 : 0 ;
element . flexibleHeight = expand ? 1 : 0 ;
// preferredWidth/Height를 -1로 설정하면 LayoutGroup이 크기를 결정하도록 합니다.
// expand가 false일 때는 기존 값을 유지하거나 특정 값으로 설정할 수 있습니다. 여기서는 기존 값을 유지하도록 합니다.
if ( expand )
{
element . preferredWidth = - 1 ;
element . preferredHeight = - 1 ;
}
}
}
}
/// <summary>
/// LayoutGroup의 모든 직접 자식 요소에 대해 LayoutElement의 flexibleWidth를 설정하여 가로 방향으로 강제 확장/축소하도록 합니다.
/// </summary>
/// <param name="layoutGroup">설정을 적용할 LayoutGroup입니다.</param>
/// <param name="expand">true로 설정하면 자식 요소가 사용 가능한 가로 공간을 채우도록 확장되고 (flexibleWidth = 1), false면 축소됩니다 (flexibleWidth = 0).</param>
/// <remarks>
/// [초보자 설명]
/// 'flexibleWidth'가 1이면 요소는 LayoutGroup 내의 가용 너비를 다른 flexible 요소들과 나눠 가집니다.
/// 'expand'가 true일 때 'preferredWidth'는 -1로 설정되어 너비가 유연하게 조절됩니다.
/// 각 직접 자식에 LayoutElement 컴포넌트가 있어야 합니다.
/// </remarks>
/// <example>
/// <code>
/// public HorizontalLayoutGroup myHLayoutGroup;
/// void MakeChildrenExpandWidth()
/// {
/// myHLayoutGroup.SetChildForceExpandWidth(true);
/// }
/// </code>
/// </example>
public static void SetChildForceExpandWidth ( this UnityEngine . UI . LayoutGroup layoutGroup , bool expand )
{
if ( layoutGroup = = null ) throw new ArgumentNullException ( nameof ( layoutGroup ) ) ;
for ( int i = 0 ; i < layoutGroup . transform . childCount ; i + + )
{
Transform child = layoutGroup . transform . GetChild ( i ) ;
LayoutElement element = child . GetComponent < LayoutElement > ( ) ;
if ( element ! = null )
{
element . flexibleWidth = expand ? 1 : 0 ;
if ( expand )
{
element . preferredWidth = - 1 ;
}
}
}
}
/// <summary>
/// LayoutGroup의 모든 직접 자식 요소에 대해 LayoutElement의 flexibleHeight를 설정하여 세로 방향으로 강제 확장/축소하도록 합니다.
/// </summary>
/// <param name="layoutGroup">설정을 적용할 LayoutGroup입니다.</param>
/// <param name="expand">true로 설정하면 자식 요소가 사용 가능한 세로 공간을 채우도록 확장되고 (flexibleHeight = 1), false면 축소됩니다 (flexibleHeight = 0).</param>
/// <remarks>
/// [초보자 설명]
/// 'flexibleHeight'가 1이면 요소는 LayoutGroup 내의 가용 높이를 다른 flexible 요소들과 나눠 가집니다.
/// 'expand'가 true일 때 'preferredHeight'는 -1로 설정되어 높이가 유연하게 조절됩니다.
/// 각 직접 자식에 LayoutElement 컴포넌트가 있어야 합니다.
/// </remarks>
/// <example>
/// <code>
/// public VerticalLayoutGroup myVLayoutGroup;
/// void MakeChildrenExpandHeight()
/// {
/// myVLayoutGroup.SetChildForceExpandHeight(true);
/// }
/// </code>
/// </example>
public static void SetChildForceExpandHeight ( this UnityEngine . UI . LayoutGroup layoutGroup , bool expand )
{
if ( layoutGroup = = null ) throw new ArgumentNullException ( nameof ( layoutGroup ) ) ;
for ( int i = 0 ; i < layoutGroup . transform . childCount ; i + + )
{
Transform child = layoutGroup . transform . GetChild ( i ) ;
LayoutElement element = child . GetComponent < LayoutElement > ( ) ;
if ( element ! = null )
{
element . flexibleHeight = expand ? 1 : 0 ;
if ( expand )
{
element . preferredHeight = - 1 ;
}
}
}
}
/// <summary>
/// LayoutGroup의 모든 직접 자식 요소에 대해 LayoutElement의 최소 너비와 높이를 설정합니다.
/// </summary>
/// <param name="layoutGroup">설정을 적용할 LayoutGroup입니다.</param>
/// <param name="minWidth">설정할 최소 너비입니다.</param>
/// <param name="minHeight">설정할 최소 높이입니다.</param>
/// <remarks>
/// [초보자 설명]
/// 'minWidth'와 'minHeight'는 LayoutElement가 가질 수 있는 최소 크기를 정의합니다.
/// LayoutGroup은 자식 요소들을 배치할 때 이 최소 크기보다 작게 만들지 않으려고 시도합니다.
/// 각 직접 자식에 LayoutElement 컴포넌트가 있어야 합니다.
/// </remarks>
/// <example>
/// <code>
/// public LayoutGroup myLayoutGroup;
/// void SetMinimumSizeForChildren()
/// {
/// myLayoutGroup.SetChildMinSize(50f, 30f); // 모든 자식의 최소 크기를 너비 50, 높이 30으로 설정
/// }
/// </code>
/// </example>
public static void SetChildMinSize ( this UnityEngine . UI . LayoutGroup layoutGroup , float minWidth , float minHeight )
{
if ( layoutGroup = = null ) throw new ArgumentNullException ( nameof ( layoutGroup ) ) ;
for ( int i = 0 ; i < layoutGroup . transform . childCount ; i + + )
{
Transform child = layoutGroup . transform . GetChild ( i ) ;
LayoutElement element = child . GetComponent < LayoutElement > ( ) ;
if ( element ! = null )
{
element . minWidth = minWidth ;
element . minHeight = minHeight ;
}
}
}
/// <summary>
/// LayoutGroup의 모든 직접 자식 요소에 대해 LayoutElement의 기본 너비와 높이(preferred size)를 설정합니다.
/// </summary>
/// <param name="layoutGroup">설정을 적용할 LayoutGroup입니다.</param>
/// <param name="preferredWidth">설정할 기본 너비입니다. -1로 설정하면 시스템이 계산한 값을 사용합니다.</param>
/// <param name="preferredHeight">설정할 기본 높이입니다. -1로 설정하면 시스템이 계산한 값을 사용합니다.</param>
/// <remarks>
/// [초보자 설명]
/// 'preferredWidth'와 'preferredHeight'는 LayoutElement가 기본적으로 차지하려는 크기를 나타냅니다.
/// LayoutGroup은 이 값을 기준으로 자식 요소들을 배치하지만, 공간이 부족하거나 'flexible' 속성이 설정된 경우 실제 크기는 달라질 수 있습니다.
/// 값을 -1로 설정하면 LayoutElement는 해당 차원의 기본 크기를 가지지 않고, 다른 요소나 내용에 따라 크기가 결정됩니다.
/// 각 직접 자식에 LayoutElement 컴포넌트가 있어야 합니다.
/// </remarks>
/// <example>
/// <code>
/// public LayoutGroup myLayoutGroup;
/// void SetPreferredSizeForChildren()
/// {
/// myLayoutGroup.SetChildPreferredSize(100f, 50f); // 모든 자식의 선호 크기를 너비 100, 높이 50으로 설정
/// }
/// </code>
/// </example>
public static void SetChildPreferredSize ( this UnityEngine . UI . LayoutGroup layoutGroup , float preferredWidth , float preferredHeight )
{
if ( layoutGroup = = null ) throw new ArgumentNullException ( nameof ( layoutGroup ) ) ;
for ( int i = 0 ; i < layoutGroup . transform . childCount ; i + + )
{
Transform child = layoutGroup . transform . GetChild ( i ) ;
LayoutElement element = child . GetComponent < LayoutElement > ( ) ;
if ( element ! = null )
{
element . preferredWidth = preferredWidth ;
element . preferredHeight = preferredHeight ;
}
}
}
/// <summary>
/// LayoutGroup의 모든 직접 자식 요소에 대해 LayoutElement의 유연한 너비와 높이(flexible size)를 설정합니다.
/// </summary>
/// <param name="layoutGroup">설정을 적용할 LayoutGroup입니다.</param>
/// <param name="flexibleWidth">설정할 유연한 너비 값입니다. 보통 0 (확장 안 함) 또는 1 (확장 함)을 사용합니다.</param>
/// <param name="flexibleHeight">설정할 유연한 높이 값입니다. 보통 0 (확장 안 함) 또는 1 (확장 함)을 사용합니다.</param>
/// <remarks>
/// [초보자 설명]
/// 'flexibleWidth'와 'flexibleHeight' 값은 LayoutGroup 내에서 여유 공간이 있을 때 해당 요소가 얼마나 그 공간을 차지할지를 결정합니다.
/// 값이 0이면 여유 공간을 차지하지 않고, 0보다 큰 값이면 다른 flexible 요소들과 비례하여 공간을 나눠 가집니다.
/// 예를 들어, 모든 자식의 flexibleWidth를 1로 설정하면 모든 자식이 동일한 비율로 가로 여유 공간을 나눠 갖습니다.
/// 각 직접 자식에 LayoutElement 컴포넌트가 있어야 합니다.
/// </remarks>
/// <example>
/// <code>
/// public LayoutGroup myLayoutGroup;
/// void SetFlexibleSizeForChildren()
/// {
/// myLayoutGroup.SetChildFlexibleSize(1f, 0f); // 모든 자식이 가로로 유연하게 확장되지만, 세로는 아님
/// }
/// </code>
/// </example>
public static void SetChildFlexibleSize ( this UnityEngine . UI . LayoutGroup layoutGroup , float flexibleWidth , float flexibleHeight )
{
if ( layoutGroup = = null ) throw new ArgumentNullException ( nameof ( layoutGroup ) ) ;
for ( int i = 0 ; i < layoutGroup . transform . childCount ; i + + )
{
Transform child = layoutGroup . transform . GetChild ( i ) ;
LayoutElement element = child . GetComponent < LayoutElement > ( ) ;
if ( element ! = null )
{
element . flexibleWidth = flexibleWidth ;
element . flexibleHeight = flexibleHeight ;
}
}
}
/// <summary>
/// LayoutGroup의 RectTransform 크기를 지정된 부모 RectTransform의 크기에 맞춥니다.
/// </summary>
/// <param name="layoutGroup">크기를 조절할 LayoutGroup입니다.</param>
/// <param name="parentRectTransform">기준이 될 부모 RectTransform입니다.</param>
/// <remarks>
/// [초보자 설명]
2025-07-09 19:33:00 +09:00
/// 이 메서드는 LayoutGroup 자체의 UI 영역 크기(`sizeDelta`)를 `parentRectTransform`의 실제 렌더링 크기(`size.width`, `size.height`)와 동일하게 만듭니다.
2025-06-17 20:19:06 +09:00
/// LayoutGroup의 앵커 설정에 따라 `sizeDelta`의 의미가 달라질 수 있지만, 일반적으로 이 메서드는 LayoutGroup이 부모의 영역을 꽉 채우도록 만듭니다.
/// </remarks>
/// <example>
/// <code>
/// public LayoutGroup myLayoutGroup;
/// public RectTransform parentPanel; // myLayoutGroup을 이 패널에 맞추고 싶음
///
/// void FitLayoutToPanel()
/// {
/// if (myLayoutGroup != null && parentPanel != null)
/// {
/// myLayoutGroup.FitToParent(parentPanel);
/// }
/// }
/// </code>
/// </example>
public static void FitToParent ( this UnityEngine . UI . LayoutGroup layoutGroup , UnityEngine . RectTransform parentRectTransform )
{
if ( layoutGroup = = null ) throw new ArgumentNullException ( nameof ( layoutGroup ) ) ;
if ( parentRectTransform = = null ) throw new ArgumentNullException ( nameof ( parentRectTransform ) ) ;
var rectTransform = layoutGroup . GetComponent < UnityEngine . RectTransform > ( ) ;
if ( rectTransform = = null )
{
Debug . LogError ( "LayoutGroupEx.FitToParent: LayoutGroup does not have a RectTransform component." , layoutGroup ) ;
return ;
}
rectTransform . sizeDelta = new UnityEngine . Vector2 ( parentRectTransform . rect . width , parentRectTransform . rect . height ) ;
}
/// <summary>
/// LayoutGroup의 RectTransform 넓이와 높이를 모든 자식 요소들을 포함하는 데 필요한 선호 높이(preferred width, height)에 맞춥니다.
/// </summary>
/// <param name="layoutGroup">넓이, 높이를 조절할 LayoutGroup입니다.</param>
/// <remarks>
/// [초보자 설명]
/// 이 메서드는 먼저 `LayoutRebuilder.ForceRebuildLayoutImmediate`를 호출하여 LayoutGroup과 그 자식들의 레이아웃 정보를 최신 상태로 강제 업데이트합니다.
/// 그런 다음 `LayoutUtility.GetPreferredWidth, LayoutUtility.GetPreferredHeight`를 사용하여 모든 자식들을 배치하는 데 필요한 전체 넓이, 높이를 계산하고,
/// LayoutGroup의 `RectTransform`의 넓이, 높이(`sizeDelta.x`, `sizeDelta.y`)를 이 계산된 선호 넓이, 높이로 설정합니다.
/// 이는 주로 ScrollView 내부의 Content Panel처럼 내용에 따라 넓이, 높이가 동적으로 변해야 하는 UI에 유용합니다.
/// </remarks>
/// <example>
/// <code>
/// public VerticalLayoutGroup contentPanelLayoutGroup; // ScrollView의 Content Panel에 붙어있는 LayoutGroup
///
/// public void AddNewItemToContent()
/// {
/// // (새로운 아이템을 contentPanelLayoutGroup의 자식으로 추가하는 코드...)
/// // Instantiate(newItemPrefab, contentPanelLayoutGroup.transform);
///
/// // 자식들이 추가/변경되었으므로 Content Panel의 높이를 자식들에 맞게 조절
/// contentPanelLayoutGroup.FitToChildren();
/// }
/// </code>
/// </example>
public static void FitToChildren ( this UnityEngine . UI . LayoutGroup layoutGroup , bool width = true , bool height = true ) // 이름 변경: FitToChdildren -> FitHeightToChildren
{
if ( layoutGroup = = null ) throw new ArgumentNullException ( nameof ( layoutGroup ) ) ;
RectTransform rectTransform = layoutGroup . GetComponent < RectTransform > ( ) ;
if ( rectTransform ! = null )
{
LayoutRebuilder . ForceRebuildLayoutImmediate ( rectTransform ) ;
Vector2 vector = new Vector2 ( rectTransform . sizeDelta . x , rectTransform . sizeDelta . y ) ;
if ( width )
{
float preferredWidth = LayoutUtility . GetPreferredWidth ( rectTransform ) ;
vector . x = preferredWidth ;
}
if ( height )
{
float preferredHeight = LayoutUtility . GetPreferredHeight ( rectTransform ) ;
vector . y = preferredHeight ;
}
rectTransform . sizeDelta = vector ;
}
else
{
Debug . LogError ( "LayoutGroupEx.FitHeightToChildren: LayoutGroup does not have a RectTransform component." , layoutGroup ) ;
}
}
}
}