Search Unity

Accordion Type Layout

Discussion in 'Unity UI (uGUI) & TextMesh Pro' started by Hotshot10101, Oct 2, 2014.

  1. Hotshot10101

    Hotshot10101

    Joined:
    Jun 23, 2012
    Posts:
    112
    I am trying to make a GUI using the new 4.6 GUI components. This screen needs to be able to expand and contract depending on the values that the user enters.

    For example if the user checks a certain checkbox, several more input fields need to show up. If they uncheck that box, those input fields need to be hidden.

    I am trying to do it with the auto layout components, but can't get it to work.

    Does anyone know how to make this happen?
     
  2. ChoMPi

    ChoMPi

    Joined:
    Jul 11, 2013
    Posts:
    111
    Hey,

    After reading your topic i had this urge to see if accordion effect could be achieved with the gui layout elements so i decided to give it a shot and here are my results.



    Also i am attaching the project from the video to this post, you guys feel free to use, share or do whatever you feel like with it : ]
     

    Attached Files:

    baco73, C1CC10X, ffollett and 9 others like this.
  3. Hotshot10101

    Hotshot10101

    Joined:
    Jun 23, 2012
    Posts:
    112
    I love this community. Thank you. I will have to modify a little bit because I need to have multiple sections open at the same time. Maybe that is called a collapsible and not an accordion.

    Great job!
     
    GibTreaty likes this.
  4. ChoMPi

    ChoMPi

    Joined:
    Jul 11, 2013
    Posts:
    111
    Hey,

    I've made a collapsible version that functions just like the accordion.
     

    Attached Files:

    C1CC10X, ffollett, Mr-Logan and 3 others like this.
  5. Melang

    Melang

    Joined:
    Mar 30, 2014
    Posts:
    166
    Thanks for making this ChoMPi, I've added this to the Useful 4.6 Scripts thread, hope you don't mind. :)
     
  6. ChoMPi

    ChoMPi

    Joined:
    Jul 11, 2013
    Posts:
    111
    No problem.
     
  7. dkely

    dkely

    Joined:
    Mar 3, 2014
    Posts:
    22
    I used the collapsible version along with knowledge from this tutorial on layouts to try solve the same issue as you had but i am still having difficulty. I was hoping i could add a cell on the gird that would contain a less constrained mix of buttons, labels and input boxes. This box will not collapse however.

    Using the downloadable example, could you tell me how i would add, for example, a button that is 40x40 units large when expanded then disappears to show only the heading when closed?

    Thanks in advance to anyone who can help me with this.
     
  8. Keyjin

    Keyjin

    Joined:
    Nov 28, 2013
    Posts:
    16
    I added on the Accordion scripts that you can't click again on the Tab.

    UIAccordion.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. namespace UnityEngine.UI
    5. {
    6.     [RequireComponent(typeof(VerticalLayoutGroup)), RequireComponent(typeof(ContentSizeFitter)), RequireComponent(typeof(ToggleGroup))]
    7.     public class UIAccordion : MonoBehaviour {
    8.      
    9.         public enum Transition
    10.         {
    11.             Instant,
    12.             Tween
    13.         }
    14.      
    15.         [SerializeField] private Transition m_Transition = Transition.Instant;
    16.         [SerializeField] private float m_TransitionDuration = 0.3f;
    17.         private Transform m_activeAccrodionElement;
    18.  
    19.         /// <summary>
    20.         /// Gets or sets the transition.
    21.         /// </summary>
    22.         /// <value>The transition.</value>
    23.         public Transition transition
    24.         {
    25.             get { return this.m_Transition; }
    26.             set { this.m_Transition = value; }
    27.         }
    28.      
    29.         /// <summary>
    30.         /// Gets or sets the duration of the transition.
    31.         /// </summary>
    32.         /// <value>The duration of the transition.</value>
    33.         public float transitionDuration
    34.         {
    35.             get { return this.m_TransitionDuration; }
    36.             set { this.m_TransitionDuration = value; }
    37.         }
    38.  
    39.         /// <summary>
    40.         /// Gets or sets the active Accrodion Element.
    41.         /// </summary>
    42.         /// <value>The active Accrodion Element</value>
    43.         public Transform activeAccrodionElement
    44.         {
    45.             get { return this.m_activeAccrodionElement; }
    46.             set { this.m_activeAccrodionElement = value; }
    47.         }
    48.     }
    49. }
    UIAccordionElement.cs
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.Events;
    4. using UnityEngine.UI.Tweens;
    5. using System;
    6. using System.Collections;
    7.  
    8. namespace UnityEngine.UI
    9. {
    10.     [RequireComponent(typeof(RectTransform)), RequireComponent(typeof(LayoutElement))]
    11.     public class UIAccordionElement : Toggle {
    12.  
    13.         [SerializeField] private float m_MinHeight = 18f;
    14.        
    15.         private UIAccordion m_Accordion;
    16.         private RectTransform m_RectTransform;
    17.         private LayoutElement m_LayoutElement;
    18.        
    19.         [NonSerialized]
    20.         private readonly TweenRunner<FloatTween> m_FloatTweenRunner;
    21.        
    22.         protected UIAccordionElement()
    23.         {
    24.             if (this.m_FloatTweenRunner == null)
    25.                 this.m_FloatTweenRunner = new TweenRunner<FloatTween>();
    26.            
    27.             this.m_FloatTweenRunner.Init(this);
    28.         }
    29.        
    30.         protected override void Awake()
    31.         {
    32.             base.Awake();
    33.             base.transition = Transition.None;
    34.             base.toggleTransition = ToggleTransition.None;
    35.             this.m_Accordion = this.gameObject.GetComponentInParent<UIAccordion>();
    36.             this.m_RectTransform = this.transform as RectTransform;
    37.             this.m_LayoutElement = this.gameObject.GetComponent<LayoutElement>();
    38.             if(this.isOn)
    39.                 this.m_Accordion.activeAccrodionElement = this.m_RectTransform;
    40.             this.onValueChanged.AddListener(OnValueChanged);
    41.         }
    42.        
    43.         protected override void OnValidate()
    44.         {
    45.             base.OnValidate();
    46.            
    47.             if (this.group == null)
    48.             {
    49.                 ToggleGroup tg = this.GetComponentInParent<ToggleGroup>();
    50.                
    51.                 if (tg != null)
    52.                 {
    53.                     this.group = tg;
    54.                 }
    55.             }
    56.            
    57.             LayoutElement le = this.gameObject.GetComponent<LayoutElement>();
    58.            
    59.             if (le != null)
    60.             {
    61.                 if (this.isOn)
    62.                 {
    63.                     le.preferredHeight = -1f;
    64.                 }
    65.                 else
    66.                 {
    67.                     le.preferredHeight = this.m_MinHeight;
    68.                 }
    69.             }
    70.         }
    71.        
    72.         public void OnValueChanged(bool state)
    73.         {
    74.             if (this.m_LayoutElement == null)
    75.                 return;
    76.             if (this.m_Accordion.activeAccrodionElement == this.m_RectTransform && state == true)
    77.                 return;
    78.             this.m_Accordion.activeAccrodionElement = this.m_RectTransform;
    79.  
    80.             UIAccordion.Transition transition = (this.m_Accordion != null) ? this.m_Accordion.transition : UIAccordion.Transition.Instant;
    81.            
    82.             if (transition == UIAccordion.Transition.Instant)
    83.             {
    84.                 if (state)
    85.                 {
    86.                     this.m_LayoutElement.preferredHeight = -1f;
    87.                 }
    88.                 else
    89.                 {
    90.                     this.m_LayoutElement.preferredHeight = this.m_MinHeight;
    91.                 }
    92.             }
    93.             else if (transition == UIAccordion.Transition.Tween)
    94.             {
    95.                 if (state)
    96.                 {
    97.                     this.StartTween(this.m_MinHeight, this.GetExpandedHeight());
    98.                 }
    99.                 else
    100.                 {
    101.                     this.StartTween(this.m_RectTransform.rect.height, this.m_MinHeight);
    102.                 }
    103.             }
    104.         }
    105.        
    106.         protected float GetExpandedHeight()
    107.         {
    108.             if (this.m_LayoutElement == null)
    109.                 return this.m_MinHeight;
    110.            
    111.             float originalPrefH = this.m_LayoutElement.preferredHeight;
    112.             this.m_LayoutElement.preferredHeight = -1f;
    113.             float h = LayoutUtility.GetPreferredHeight(this.m_RectTransform);
    114.             this.m_LayoutElement.preferredHeight = originalPrefH;
    115.            
    116.             return h;
    117.         }
    118.        
    119.         protected void StartTween(float startFloat, float targetFloat)
    120.         {
    121.             float duration = (this.m_Accordion != null) ? this.m_Accordion.transitionDuration : 0.3f;
    122.            
    123.             FloatTween info = new FloatTween
    124.             {
    125.                 duration = duration,
    126.                 startFloat = startFloat,
    127.                 targetFloat = targetFloat
    128.             };
    129.             info.AddOnChangedCallback(SetHeight);
    130.             info.ignoreTimeScale = true;
    131.             this.m_FloatTweenRunner.StartTween(info);
    132.         }
    133.        
    134.         protected void SetHeight(float height)
    135.         {
    136.             if (this.m_LayoutElement == null)
    137.                 return;
    138.                
    139.             this.m_LayoutElement.preferredHeight = height;
    140.         }
    141.     }
    142. }
    143.  
    If you have any ideas how it could be better solved please feel free to share :) .

    -Keyjin
     
  9. mimminito

    mimminito

    Joined:
    Feb 10, 2010
    Posts:
    779
    If you have a child of an AccordianElement, such as an image, which then has a child, such as some text, when its collapsed the text is still shown. Any way to fix this?
     
  10. ChoMPi

    ChoMPi

    Joined:
    Jul 11, 2013
    Posts:
    111
    You could use a mask.
     
    Harinezumi likes this.
  11. Suave Zombie

    Suave Zombie

    Joined:
    May 11, 2013
    Posts:
    2
    Has this been tested on Unity 5?
     
  12. Yukichu

    Yukichu

    Joined:
    Apr 2, 2013
    Posts:
    417
    Yes, works wonderfully.
     
  13. Suave Zombie

    Suave Zombie

    Joined:
    May 11, 2013
    Posts:
    2
    I seem to be having trouble getting it to work then.

    I want a persistent label present like the heading in the demo video, but with nested buttons that are shown when the accordion is on. I can't seem to get the child elements to hide when the accordion is turned off.

    EDIT: Used the collapsible one linked above instead. Its working great! I hope it works well inside a dynamically sized scroll pane.
     
    Last edited: Mar 27, 2015
  14. generic2709

    generic2709

    Joined:
    Dec 2, 2014
    Posts:
    1
    Hi Chompi. the Collapsible version Works fine. But the transition happens in both directions. anyway to make sure that when it expands, it is downward.
     
  15. Catharticism

    Catharticism

    Joined:
    Dec 19, 2014
    Posts:
    1
    Maybe this is a bit late for you, but if you want it to expand downward, set your Y Pivot on the rect transform containing the Accordion to 1 instead of 0.5.
     
  16. Dancan1979

    Dancan1979

    Joined:
    Nov 6, 2015
    Posts:
    1
    Hi, this is a amazing project. But I'm having some problens to build the project. I recived the following message:

    Assets/Accordion/Scripts/UIAccordionElement.cs(40,41): error CS0115: `UnityEngine.UI.UIAccordionElement.OnValidate()' is marked as an override but no suitable method found to override

    Anyone know how to solve it?
    Thanks!
     
  17. prinzwurstbrot

    prinzwurstbrot

    Joined:
    Jul 28, 2015
    Posts:
    7
    When I have a button as the top element, instead of a simple text, the event is beeing caught by that button. So the accorion won't open. I need that button to define the gameobject im working on. Is there a simple solution to that ?
     
  18. BabilinApps

    BabilinApps

    Joined:
    Dec 28, 2014
    Posts:
    40
    You need to use
    Code (CSharp):
    1. using UnityEngine.UI.Extensions.Tweens;
    for Unity 5.3
     
  19. davimusprime

    davimusprime

    Joined:
    Feb 23, 2016
    Posts:
    10
    Using Unity 5.3.2f, when I try and compile I get:
    error CS0115: `UnityEngine.UI.UIAccordionElement.OnValidate()' is marked as an override but no suitable method found to override
     
    rhhp likes this.
  20. TheValar

    TheValar

    Joined:
    Nov 12, 2012
    Posts:
    752
    I may just be an idiot but I cannot get this to work. Anything particular I need to know about the heirarchy or any specific settings. I have an Accordion Group with an Accordion Element child. How does the setup need to be under accordion Element?

    Edit: Ok I got it working
     
    Last edited: May 4, 2016
  21. KaiserAfini

    KaiserAfini

    Joined:
    Aug 6, 2013
    Posts:
    7
    Could you please explain how you did it ? Its puzzling me as well
     
  22. rhhp

    rhhp

    Joined:
    Jan 29, 2018
    Posts:
    2
    Using Unity 5.5.1, when I try and compile I get:
    error CS0115: `UnityEngine.UI.UIAccordionElement.OnValidate()' is marked as an override but no suitable method found to override[/QUOTE]
    can anyone help me?
     
  23. fafase

    fafase

    Joined:
    Jul 3, 2012
    Posts:
    158
    Could anyone explain how it is used?
    The video does not show much and I don't get anything happening
     
  24. tabbakka

    tabbakka

    Joined:
    Nov 19, 2018
    Posts:
    2
    can anyone help me?[/QUOTE]
    I have the same issue, just remove the "protected override" and "base.OnValidate()" and it will work.
     
  25. tabbakka

    tabbakka

    Joined:
    Nov 19, 2018
    Posts:
    2
    So, i have a question is it possible to put some groups of ui elements inside the accordion. i try to put a few sliders, but it tottaly doenst work )
    Any ideas?
     
  26. shadowlord1

    shadowlord1

    Joined:
    Aug 15, 2014
    Posts:
    28
    Any update on this?
    It seems the Accordion can only handle a single text inside of it, but not a group of other UI elements.
     
  27. Proxi

    Proxi

    Joined:
    Jan 15, 2015
    Posts:
    5
    @shadowlord1 what I did was adding a Mask component to the gameobject with the UICollapsible script on it (when using the Collapsible alternative from ChoMPi). That way you can infinitely nest objects in the Text gameobject that will be hidden on closing the headings.
     
  28. shadowlord1

    shadowlord1

    Joined:
    Aug 15, 2014
    Posts:
    28
    That's great! Thank you very much @Proxi
     
  29. onizuka92

    onizuka92

    Joined:
    Oct 24, 2014
    Posts:
    3
    would anyone be willing to show us how to get it to work correctly? seems like half of us know the secret sauce and the other half is asking for help. the video while simple isn't very great at explaining why I cant see it change. the code runs i put a debug statement in it and from there im just lost.
     
  30. shadowlord1

    shadowlord1

    Joined:
    Aug 15, 2014
    Posts:
    28
    Sure my friend!

    1- First, you create a gameObject... On the example, it's called "Accordion" and then its children (also empty gameobjects)


    2- The "Accordion" gameobject, have the following components:


    3- The children have the following components each one:


    4- Inside each children, it has a header title and its description


    5- The "Headline" contains a Text componentwith the header title and a Layout Element with minHeight of 18
    6- The "Text" gameobject just contains a Text component with the description that is seen once we open this Tab.


    Let me know if you have any doubts
     
  31. onizuka92

    onizuka92

    Joined:
    Oct 24, 2014
    Posts:
    3
    thank this is really great. i think the secret sauce came from understanding how the child controls size and where content size fitter needs to be placed in order to work. and why an object may need a layout element to determine the final sizing for the content size fitter.
     
  32. shadowlord1

    shadowlord1

    Joined:
    Aug 15, 2014
    Posts:
    28
    Hello onizuka92!
    Yes, it took me some time to understand this.
    I've decided to make a small explanation here, so I can help and also "solidify" what I learn so far: ;)

    Content Size Fitter:


    Normally when we add an UI element containing a RectTransform, (Like a text, for example), we have to manually expand or shrink it's corners to fit the text inside of it... (When we change an UI element, like a text, the RectTransform have all necessary informations... But during edit-mode the element doesn't "listen" to these changes... So we have to do it manually). Example:



    However, you may want this rectangle to be automatically sized to fit the content (Let's suppose... There's a Text element receiving the score in Play mode (runtime)... We don't know the 'length' of this text, so it may be larger or smaller... If it's larger, we'd want this to not cut the text, but instead, expand to fit it). In order to do that automatically.... We can add a Content Size Fitter component to this Text GameObject. This component will always be listening to all the changes and will force the RectTransform to adapt to these changes in realtime. (Be aware that, because of this nature of being always updating the elements, it's not recommended, for performance reasons, to over use/abuse of this function)
    Take a look at how the RectTransform keep updating its width as I type a new text:


    On this example, the "Horizontal Fit", is set to Preferred Size. On the Content Size Fitter Component.


    This component have 3 options:
    Unconstrained - "This one basically means "I don't care, change the size by yourself. I won't :mad:" (Is like the OFF/Turned Off option)
    Min Size - This one is like "Okay, what's the minimum width/height this RectTransform can get? 0?" (I didn't quite understand how to use it correctly yet)
    Preferred Size - "What's the best size to fit the content? This size is my favorite, let's take it!" :D

    -------------------------------------------------------------------------------------
    Layout Element:
    Sometimes you may want to specificy a size to some item in your UI... Like "Hey, the favorite size of this card would be 250"... Or maybe "When all other cards got their sizes, I want this little thing here occupy the remaining space that was left..."...
    The Layout element shines here. It's job is to ensure specific sizes of UI elements or at least, how to get a flexible size. (more info can be found at the docs: https://docs.unity3d.com/Manual/script-LayoutElement.html)
    ------------------------------------------------------------------------------------

    Understanding this, is the base to understand how the "Vertical Layout Group" or the "Horizontal Layout Group" works...

    Let me know if you have any doubts...If someone would like to correct my mistakes or add more infos, be welcome to share. :D


     
  33. ookk47oo

    ookk47oo

    Joined:
    Mar 17, 2017
    Posts:
    26
    Amazing work.
    I made some change so that it will still support the checkmark graphic. And also,I added an event to this script to know the selected element's index. If someone is interested ,check this repo: https://github.com/0kk470/Unity_AccordionUI.

    Hope to help you. :)

    Edit: The selected element will not shrink if the togglegroup's allowSwitchOff is false.So I used another bool value to record elements state when allowSwitchOff is false.
     
    Last edited: Apr 12, 2019
  34. ookk47oo

    ookk47oo

    Joined:
    Mar 17, 2017
    Posts:
    26
    OnValidate is only available in the editor.