Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Resolved Scroll View with varying height layout elements

Discussion in 'UGUI & TextMesh Pro' started by matzomat, Aug 5, 2023.

  1. matzomat

    matzomat

    Joined:
    Mar 4, 2018
    Posts:
    63
    Hey Guys,

    I'm stuck and need your help please.

    I'm creating a (vertical) scroll View with prefabs inside. These prefabs consist of an object, that contains a TMP Text of varying height and a TMP input field that can also grow in height depending on the amount of text inside.

    I can't get the vertical layout group in the content object of the scroll view to arrange the childs. The reason is that the prefabs never seem to know their own size.

    I tried a lot from content size fitter (yeah, I know) to (a lot of) layout elements and so on. And yes, several tutorials. Didn't find any solution. Ideas?
     
  2. matzomat

    matzomat

    Joined:
    Mar 4, 2018
    Posts:
    63
    Solution found.

    First of all, thanks to: https://forum.unity.com/threads/content-size-fitter-refresh-problem.498536/
    That's where I basically stole it.

    Components of solution:
    1. I built my own contentsizefitter, that actually worked with texts. I put it on the top level of the prefab that will later spawn as the contents of the scrollwindow.
    2. I built a refresh, based on the above forum entry, RefreshContentFitter. I put this on top of the scrollview.
    3. "OnEdit" of the textfield inside the prefab inside the scroll window calls a refresh on my CustomContentSizeFitter.
    4. If CustomContentSizeFitter finds a difference in size, he takes the first RefreshContentFitter he finds in his parents and calles it.

    When reading the code, pls remember that I'm a hobbyist and give me some slack. Any feedback, especially about optimization is appreciated though.

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using TMPro;
    5.  
    6. public class CustomContentSizeFitter : MonoBehaviour
    7. {
    8.     [Tooltip("List of objects to consider for size fitting")]
    9.     public List<RectTransform> objectsToFit;
    10.     private RectTransform rectTransform;
    11.     private float lastTotalHeight = 0f;
    12.     private float lastMaxWidth = 0f;
    13.     private float maxWidth = 0f;
    14.     private float totalHeight = 0f;
    15.     private Vector2 sizeDelta;
    16.     private ContentFitterRefresh parentContentFitterRefresh;
    17.  
    18.     private void Awake()
    19.     {
    20.         rectTransform = GetComponent<RectTransform>();
    21.         parentContentFitterRefresh = FindParentContentFitterRefresh(gameObject.transform);
    22.     }
    23.  
    24.     public void OnEnable()
    25.     {
    26.         RefreshSize();
    27.     }
    28.  
    29.     public void Update()
    30.     {
    31.         RefreshSize();
    32.     }
    33.  
    34.     public void RefreshSize()
    35.     {
    36.         if (objectsToFit == null || objectsToFit.Count == 0) return;
    37.      
    38.      
    39.         maxWidth = 0f;
    40.         totalHeight = 0f;
    41.      
    42.  
    43.         foreach (var rect in objectsToFit)
    44.         {
    45.             if (rect == null) continue;
    46.  
    47.             Vector2 size = rect.sizeDelta;
    48.  
    49.             if (size.x > maxWidth)
    50.             {
    51.                 maxWidth = size.x;
    52.             }
    53.  
    54.             totalHeight += size.y;
    55.         }
    56.  
    57.         sizeDelta.x = maxWidth;
    58.         sizeDelta.y = totalHeight;
    59.         rectTransform.sizeDelta = sizeDelta;
    60.         if ((lastTotalHeight!=totalHeight)||(lastMaxWidth!=maxWidth))
    61.         {
    62.             lastMaxWidth = maxWidth;
    63.             lastTotalHeight = totalHeight;
    64.             parentContentFitterRefresh?.RefreshContentFitters();
    65.         }
    66.  
    67.  
    68.     }
    69.  
    70.     public ContentFitterRefresh FindParentContentFitterRefresh(Transform startTransform)
    71.     {
    72.         Transform currentParent = startTransform.parent;
    73.  
    74.         while (currentParent != null)
    75.         {
    76.             ContentFitterRefresh contentFitterRefresh = currentParent.GetComponent<ContentFitterRefresh>();
    77.             if (contentFitterRefresh != null)
    78.             {
    79.                 return(contentFitterRefresh);
    80.             }
    81.             currentParent = currentParent.parent;
    82.         }
    83.  
    84.         Debug.Log("No parent ContentFitterRefresh found!");
    85.         return (null);
    86.     }
    87.  
    88. }
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class ContentFitterRefresh : MonoBehaviour
    7. {
    8.     public bool trigger = false;
    9.     private void Awake()
    10.     {
    11.         RefreshContentFitters();
    12.     }
    13.  
    14.     public void OnEnable()
    15.     {
    16.         RefreshContentFitters();
    17.     }
    18.  
    19.  
    20. public void RefreshContentFitters()
    21.     {
    22.         var rectTransform = (RectTransform)transform;
    23.         RefreshContentFitter(rectTransform);
    24.     }
    25.  
    26.     private void RefreshContentFitter(RectTransform transform)
    27.     {
    28.         if (transform == null || !transform.gameObject.activeSelf)
    29.         {
    30.             return;
    31.         }
    32.  
    33.         foreach (RectTransform child in transform)
    34.         {
    35.             RefreshContentFitter(child);
    36.         }
    37.  
    38.         var layoutGroup = transform.GetComponent<LayoutGroup>();
    39.         var contentSizeFitter = transform.GetComponent<ContentSizeFitter>();
    40.         if (layoutGroup != null)
    41.         {
    42.             layoutGroup.SetLayoutHorizontal();
    43.             layoutGroup.SetLayoutVertical();
    44.         }
    45.  
    46.         if (contentSizeFitter != null)
    47.         {
    48.             LayoutRebuilder.ForceRebuildLayoutImmediate(transform);
    49.         }
    50.     }
    51. }
    52.  
    53.