Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Nested Scrollrect?

Discussion in 'UGUI & TextMesh Pro' started by ssawyer, Sep 15, 2014.

  1. antpaw

    antpaw

    Joined:
    Oct 25, 2016
    Posts:
    8
  2. RoguePrimitive

    RoguePrimitive

    Joined:
    Sep 11, 2017
    Posts:
    3
    I got worried that can I implement this script into my project... HAHA! :) Easy and super-good! Thank you @CaptainSchnittchen
     
  3. A_Eniwent_S

    A_Eniwent_S

    Joined:
    Oct 23, 2018
    Posts:
    4
    Hi there.

    Please, help me.
    If I have something like this, where should I put this script and how can I configure it?

    Placing the script on page 2 or its content does not work.



     

    Attached Files:

  4. sandolkakos

    sandolkakos

    Joined:
    Jun 3, 2009
    Posts:
    273
    Are you using the script from @CaptainSchnittchen ?
     
  5. A_Eniwent_S

    A_Eniwent_S

    Joined:
    Oct 23, 2018
    Posts:
    4
    Hi. Yes, script from CaptainSchnittchen.
     
  6. sandolkakos

    sandolkakos

    Joined:
    Jun 3, 2009
    Posts:
    273
    You need only create new ScrollViews as children of your Default ScrollView, but on the children, you need to use the ScrollRectEx script (instead of the default ScrollRect script) because it will route the drag events to parent.

    In this example, the Parent has only Vertical movement while every child has only Horizontal movement:

    ScrollRectEx.PNG
     
    A_Eniwent_S likes this.
  7. A_Eniwent_S

    A_Eniwent_S

    Joined:
    Oct 23, 2018
    Posts:
    4
    Thank you very much! Everything worked out!
     
    sandolkakos likes this.
  8. sandolkakos

    sandolkakos

    Joined:
    Jun 3, 2009
    Posts:
    273
    You're welcome ;)
     
  9. cp-

    cp-

    Joined:
    Mar 29, 2018
    Posts:
    78
    Here's a version not creating all those anonymous functions but using the ExecuteEvents API provided by Unity.

    To me it seems to be the more "correct" way of implementing events propagating upwards. Also it may be more GC-friendly.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.UI;
    4. using System;
    5. using UnityEngine.EventSystems;
    6.  
    7. public class ScrollRectEx : ScrollRect
    8. {
    9.     private bool routeToParent = false;
    10.  
    11.     /// <summary>
    12.     /// Always route initialize potential drag event to parents
    13.     /// </summary>
    14.     public override void OnInitializePotentialDrag(PointerEventData eventData)
    15.     {
    16.         ExecuteEvents.ExecuteHierarchy(transform.parent.gameObject, eventData, ExecuteEvents.initializePotentialDrag);
    17.         base.OnInitializePotentialDrag(eventData);
    18.     }
    19.  
    20.     /// <summary>
    21.     /// Drag event
    22.     /// </summary>
    23.     public override void OnDrag(UnityEngine.EventSystems.PointerEventData eventData)
    24.     {
    25.         if (routeToParent)
    26.             ExecuteEvents.ExecuteHierarchy(transform.parent.gameObject, eventData, ExecuteEvents.dragHandler);
    27.         else
    28.             base.OnDrag(eventData);
    29.     }
    30.  
    31.     /// <summary>
    32.     /// Begin drag event
    33.     /// </summary>
    34.     public override void OnBeginDrag(UnityEngine.EventSystems.PointerEventData eventData)
    35.     {
    36.         if (!horizontal && Math.Abs(eventData.delta.x) > Math.Abs(eventData.delta.y))
    37.             routeToParent = true;
    38.         else if (!vertical && Math.Abs(eventData.delta.x) < Math.Abs(eventData.delta.y))
    39.             routeToParent = true;
    40.         else
    41.             routeToParent = false;
    42.  
    43.         if (routeToParent)
    44.             ExecuteEvents.ExecuteHierarchy(transform.parent.gameObject, eventData, ExecuteEvents.beginDragHandler);
    45.         else
    46.             base.OnBeginDrag(eventData);
    47.     }
    48.  
    49.     /// <summary>
    50.     /// End drag event
    51.     /// </summary>
    52.     public override void OnEndDrag(UnityEngine.EventSystems.PointerEventData eventData)
    53.     {
    54.         if (routeToParent)
    55.             ExecuteEvents.ExecuteHierarchy(transform.parent.gameObject, eventData, ExecuteEvents.endDragHandler);
    56.         else
    57.             base.OnEndDrag(eventData);
    58.         routeToParent = false;
    59.     }
    60. }
     
    sandolkakos, _geo__, mvilano and 4 others like this.
  10. zulqarnain26

    zulqarnain26

    Joined:
    Feb 12, 2018
    Posts:
    7
  11. unity_1cewWbGw8rRVxw

    unity_1cewWbGw8rRVxw

    Joined:
    Jun 6, 2018
    Posts:
    9
    Thank You @CaptainSchnittchen you are a genius.
    I don't know how to thank you.
    Phenomenal work.
    Can not Thank you enough.
     
  12. zulqarnain26

    zulqarnain26

    Joined:
    Feb 12, 2018
    Posts:
    7
  13. Elledan3101

    Elledan3101

    Joined:
    May 24, 2013
    Posts:
    18
    your code still awesome. Even in 2k19! Thanks!
     
  14. flatsplay

    flatsplay

    Joined:
    Feb 6, 2019
    Posts:
    2
  15. Colin_MacLeod

    Colin_MacLeod

    Joined:
    Feb 11, 2014
    Posts:
    319
  16. atulyextel_26

    atulyextel_26

    Joined:
    Feb 18, 2019
    Posts:
    1
    Can you tell me how to do that plz
     
  17. sandolkakos

    sandolkakos

    Joined:
    Jun 3, 2009
    Posts:
    273
  18. LAKSHAYMAVIA

    LAKSHAYMAVIA

    Joined:
    Aug 28, 2018
    Posts:
    27
    First of all, thank you for creating this post. With the script of @cp- which is inspired by @CaptainSchnittchen. I manage to create nested scroll rect. I am attaching the Image and video so that others can achieve something like this if they want to.
    Asset use:- simple scroll (Free from asset Store) + Above scroll Rect Ex Script + Dotween(Free from asset Store).

    https://imgur.com/a/09VYwBd

     
  19. alininavcisi

    alininavcisi

    Joined:
    Mar 10, 2015
    Posts:
    8
    Thank you really, you saved my life :)
     
  20. Erikoinen

    Erikoinen

    Joined:
    Nov 10, 2014
    Posts:
    68
    @KGC Thanks a ton!
     
  21. Firestorm200

    Firestorm200

    Joined:
    Sep 29, 2013
    Posts:
    5
  22. DerLasseHenrich

    DerLasseHenrich

    Joined:
    Aug 31, 2017
    Posts:
    3
  23. nhatkhiem

    nhatkhiem

    Joined:
    Nov 7, 2014
    Posts:
    1
    @CaptainSchnittchen even 2020 is coming, that's nice solution. Thankyou very much and Merry Christmas!
     
  24. Kimuradfv

    Kimuradfv

    Joined:
    Apr 30, 2019
    Posts:
    1
    Thank you so much! this solved the problem instantly!
     
  25. twice7713

    twice7713

    Joined:
    Apr 24, 2018
    Posts:
    24
    Thank you very much, @CaptainSchnittchen
     
  26. mitaywalle

    mitaywalle

    Joined:
    Jul 1, 2013
    Posts:
    247
  27. Polo07

    Polo07

    Joined:
    Sep 10, 2018
    Posts:
    6
    Five years on... still awesome! Copied the code and it worked like a charm. You rock @CaptainSchnittchen.

    Although I had an even more complicated sructure, where I had scrollrect->scrollrect->scrollrect so had to intercept your while loop and break it where I wanted. But this was sleek!!
     
    SimonDarksideJ likes this.
  28. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,688
    FYI, ScrollRectEx and also the ScrollRectConflictManager have been part of the awesome Unity UI Extensions project (link in sig) for a long time. We recently had another big release for 2019 that's work checking out.

    Although seeing some other interesting proposed updates, so I'm going to have to review and see what if anything can be improved.
     
  29. Jumeuan

    Jumeuan

    Joined:
    Mar 14, 2017
    Posts:
    39
    This is working like a charm, try it!
     
  30. RMGK

    RMGK

    Joined:
    Sep 30, 2011
    Posts:
    75
    Alright, thats it! You just won best person of the month award. Great code, great solution. Big thank you!
     
  31. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,250
    A minor addition to @cp- 's version. I have added an editor only Reset() method to hook up Content and Viewport automatically once the component is added (saves a little bit of work on setup).

    I also added a "routeScrollEventsToParent" property to allow routing OnScoll() events to the parent scroll view. I use this to forward mouse wheel events from my horizontal child scroll view to the vertical parent.

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using System;
    5. using UnityEngine.EventSystems;
    6. #if UNITY_EDITOR
    7. using UnityEditor;
    8. using UnityEditor.UI;
    9. #endif
    10.  
    11. /// <summary>
    12. /// Use this to enable scroll views withing scroll views.
    13. /// Thanks to https://forum.unity.com/threads/nested-scrollrect.268551/page-2#post-4214161
    14. ///
    15. /// Don't forget to add a Graphics Raycaster if you put this in a sub canvas.
    16. /// </summary>
    17. public class ScrollRectEx : ScrollRect
    18. {
    19.    public bool routeScrollEventsToParent = false;
    20.    protected bool routeToParent = false;
    21.  
    22.    /// <summary>
    23.    /// Always route initialize potential drag event to parents
    24.    /// </summary>
    25.    public override void OnInitializePotentialDrag(PointerEventData eventData)
    26.    {
    27.        ExecuteEvents.ExecuteHierarchy(transform.parent.gameObject, eventData, ExecuteEvents.initializePotentialDrag);
    28.        base.OnInitializePotentialDrag(eventData);
    29.    }
    30.  
    31.    /// <summary>
    32.    /// Drag event
    33.    /// </summary>
    34.    public override void OnDrag(PointerEventData eventData)
    35.    {
    36.        if (routeToParent)
    37.            ExecuteEvents.ExecuteHierarchy(transform.parent.gameObject, eventData, ExecuteEvents.dragHandler);
    38.        else
    39.            base.OnDrag(eventData);
    40.    }
    41.  
    42.    /// <summary>
    43.    /// Begin drag event
    44.    /// </summary>
    45.    public override void OnBeginDrag(PointerEventData eventData)
    46.    {
    47.        if (!horizontal && Math.Abs(eventData.delta.x) > Math.Abs(eventData.delta.y))
    48.            routeToParent = true;
    49.        else if (!vertical && Math.Abs(eventData.delta.x) < Math.Abs(eventData.delta.y))
    50.            routeToParent = true;
    51.        else
    52.            routeToParent = false;
    53.  
    54.        if (routeToParent)
    55.            ExecuteEvents.ExecuteHierarchy(transform.parent.gameObject, eventData, ExecuteEvents.beginDragHandler);
    56.        else
    57.            base.OnBeginDrag(eventData);
    58.    }
    59.  
    60.    /// <summary>
    61.    /// End drag event
    62.    /// </summary>
    63.    public override void OnEndDrag(PointerEventData eventData)
    64.    {
    65.        if (routeToParent)
    66.            ExecuteEvents.ExecuteHierarchy(transform.parent.gameObject, eventData, ExecuteEvents.endDragHandler);
    67.        else
    68.            base.OnEndDrag(eventData);
    69.        routeToParent = false;
    70.    }
    71.  
    72.    public override void OnScroll(PointerEventData eventData)
    73.    {
    74.        if (routeScrollEventsToParent)
    75.            ExecuteEvents.ExecuteHierarchy(transform.parent.gameObject, eventData, ExecuteEvents.scrollHandler);
    76.        else
    77.            base.OnScroll(eventData);
    78.    }
    79.  
    80.  
    81.  
    82. #if UNITY_EDITOR
    83.    protected override void Reset()
    84.    {
    85.        this.viewport = this.transform.Find("Viewport").transform as RectTransform;
    86.        this.content = this.transform.Find("Viewport/Content").transform as RectTransform;
    87.        base.Reset();
    88.    }
    89. #endif
    90. }
    91.  
    92. #if UNITY_EDITOR
    93. [CustomEditor(typeof(ScrollRectEx))]
    94. public class ScrollRectNestedEditor : ScrollRectEditor
    95. {
    96.    SerializedProperty parentScrollRectProp;
    97.    GUIContent parentScrollRectGUIContent = new GUIContent("Reroute Scroll Events:", "Stop this scroll view from handling scroll events and route them to the parent instead.");
    98.  
    99.    protected override void OnEnable()
    100.    {
    101.        base.OnEnable();
    102.        parentScrollRectProp = serializedObject.FindProperty("routeScrollEventsToParent");
    103.    }
    104.  
    105.    public override void OnInspectorGUI()
    106.    {
    107.        base.OnInspectorGUI();
    108.        serializedObject.Update();
    109.        EditorGUILayout.PropertyField(parentScrollRectProp, parentScrollRectGUIContent);
    110.        serializedObject.ApplyModifiedProperties();
    111.    }
    112. }
    113. #endif
    114.  
    115.  
     
    Last edited: Mar 23, 2021
    sandolkakos likes this.
  32. sandolkakos

    sandolkakos

    Joined:
    Jun 3, 2009
    Posts:
    273
    @_geo__ , I like the idea of the Reset method to set fields automatically, but I was wondering if the #if UNITY_EDITOR check is needed, since the MonoBehaviour.Reset() method is called only in editor mode.
     
  33. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,250
    Hi, glad you like it. It may not be necessary to remove it but it's also unneeded code in the build, which I try avoid. I simply have the habbit to #ifdef everything editor related. No additional thought went into it.
     
    sandolkakos likes this.
  34. sandolkakos

    sandolkakos

    Joined:
    Jun 3, 2009
    Posts:
    273
    It makes sense. My question was more because I also use the Reset method to set fields automatically, but I dont really know if those editor methods get compiled to the final build. That would be very nice to know :)
     
  35. Magic73

    Magic73

    Joined:
    Jun 23, 2015
    Posts:
    127
    @CaptainSchnittchen Just to say: I love you ;)
     
  36. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,250
    If it's not in an editor #ifdef then it will be compiled into your build (unless your script is in an Editor folder or you use asmdefs etc. do exclude it). There really is nothing special about Reset(). It's just a method which the editor calls. I try to avoid using any method that's invoked by the editor outside of #ifdefs. - You easily end up with things working in the editor but not in build because it's being called automagically (excuse the pun) in the editor but not in builds. Unity has chosen to automatically call some methods which have very common names: OnValidate() and Reset(). I try to not use these as method names in Unity projects (even if the class is not a MonoBehaviour), simply to avoid aforementioned caveats.
     
    sandolkakos likes this.
  37. hendryshaikh2004

    hendryshaikh2004

    Joined:
    Apr 24, 2018
    Posts:
    8
    7 years later still works thx
     
  38. fraeri

    fraeri

    Joined:
    Nov 8, 2018
    Posts:
    64
    Hi All,
    So I'm just another one using the @CaptainSchnittchen Script. What can I say :D Just Awesome.

    But one question: I have a vertical Scrollview with nested horizontal ones. On Mobile no problem. But I was wondering how I can get it to work with the mouse scroll on desktop. So normal mouse-scroll should by scrolling the vertical scrollview and the horizontal mouse-scroll should only interact with the horizontal scrollviews. Currently both mouse scrolls are interacting with the specific scrollview I am hovering. So normal scroll scrolls horizontal, when over a nested horizontal.

    Any ideas how to make that happen?
     
    Last edited: May 23, 2021
  39. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,250
  40. Jkmaurya1293

    Jkmaurya1293

    Joined:
    Jun 28, 2021
    Posts:
    1
  41. SergioAAV

    SergioAAV

    Joined:
    Nov 14, 2013
    Posts:
    1
    Hello everyone!
    I would like to also try the solution from @KGC but I don't know where I should put or how to link the ScrollRectNestedEditor.cs.
    Can anyone point me the correct way? The other one, ScrollRectNested.cs I added successfully to my ScrollView.
     
  42. sandolkakos

    sandolkakos

    Joined:
    Jun 3, 2009
    Posts:
    273
    Hey @SergioAAV, I don't know if you have already figured it out, but you can create a folder with the name "Editor" in any place in your project and add the "ScrollRectNestedEditor.cs" inside it. That script is a customization for the inspector of gameObjects with the component "ScrollRectNested" attached to it.
     
  43. Musahan17

    Musahan17

    Joined:
    Aug 9, 2020
    Posts:
    5
  44. Babak-Firoozi-Yasar

    Babak-Firoozi-Yasar

    Joined:
    Aug 13, 2014
    Posts:
    9
  45. radiantboy

    radiantboy

    Joined:
    Nov 21, 2012
    Posts:
    1,632
    anyone know how to also get this working with the mousewheel? it will only use the mouse wheel between the horizontal scrollers, not anywhere on screen like the drag.
     
  46. akshay773

    akshay773

    Joined:
    Sep 26, 2017
    Posts:
    1
    The class BaseUIBehaviour is missing
     
  47. Tyrannicus100BC

    Tyrannicus100BC

    Joined:
    Apr 24, 2015
    Posts:
    5
    I wanted to followup with an even simpler approach, which is building upon @CaptainSchnittchen's excellent start.

    Instead of manually intercepting the OnDrag / OnDragEnd events and redispatching ourselves, we can actually just tell Unity EventSystem to permanently reroute the drag events by changing eventData.pointerDrag to point to the new object.

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using UnityEngine.EventSystems;
    5.  
    6. // Subclass of ScrollRect that allows two scroll views to be nested, if they scroll on different axis.
    7. // Simply use this class in the nested scroll view and it will reroute scroll events if this object can't handle it.
    8. public class ScrollRectNested : ScrollRect {
    9.     // Other scroll hander in our parents.
    10.     GameObject RerouteTarget;
    11.  
    12.     public override void OnInitializePotentialDrag(PointerEventData eventData) {
    13.         // Find potential reroute target in our parents.
    14.         RerouteTarget = ExecuteEvents.GetEventHandler<IDragHandler>(transform.parent.gameObject);
    15.  
    16.         // If a reroute target is available, dispatch this event to it.
    17.         if(RerouteTarget != null)
    18.             ExecuteEvents.ExecuteHierarchy(RerouteTarget, eventData, ExecuteEvents.initializePotentialDrag);
    19.  
    20.         // Dispatch to base class.
    21.         base.OnInitializePotentialDrag(eventData);
    22.     }
    23.  
    24.     public override void OnBeginDrag(UnityEngine.EventSystems.PointerEventData eventData) {
    25.         // Check if this drag event is off axis from what this scroll rect supports.
    26.         var offaxis = !horizontal && Math.Abs(eventData.delta.x) > Math.Abs(eventData.delta.y)
    27.             || !vertical && Math.Abs(eventData.delta.x) < Math.Abs(eventData.delta.y);
    28.  
    29.         // If the drag is off axis and a reroute object is available, then update the event handler and redispatch.
    30.         // Otherwise dispatch to base class.
    31.         if(RerouteTarget != null && offaxis) {
    32.             eventData.pointerDrag = RerouteTarget;
    33.             ExecuteEvents.ExecuteHierarchy(RerouteTarget, eventData, ExecuteEvents.beginDragHandler);
    34.         } else {
    35.             base.OnBeginDrag(eventData);
    36.         }
    37.  
    38.         // Clear out reroute target.
    39.         RerouteTarget = null;
    40.     }
    41. }
     
  48. Arkadi097

    Arkadi097

    Joined:
    Aug 2, 2022
    Posts:
    1