Nested Scrollrect?

I’m hoping to put together a Netflix-like interface for users to select the scenes in my project.

I need to be able to scroll up and down overall, but also be able to scroll rows of buttons left and right.

I can aaaaaalmost get this behavior without much work at all - I simply set up a scrollrect according to the documentation that covers the entire screen, and then made several smaller scrollrects for the horizontal rows of buttons. I made all of the buttons children of the same parent, placed the parent under the overall scrollrect in the hierarchy, and dragged that parent into the Content property of the overall scrollrect. Hopefully that makes sense.

I can scroll up and down if I touch above, below, or inbetween rows of buttons, and I can scroll horizontally if I touch the buttons. Unfortunately, I can’t scroll up and down while touching the buttons. Since the buttons take up the majority of the screenspace, it’s definitely an issue.

Is there something I’ve overlooked? Is there a better way to set this up? Is this possible in the current state of the UI system?

4 Likes

I heared you can hook into the EventSystem and assing the MouseWheel-Input to the ScrollOverall component only and prevent it from getting grabbed. Could be for 5.0 though.

1 Like

That actually sounds pretty cool! Unfortunately it doesn’t help me all that much for this project - I should have mentioned that I’m targeting tablets.

Hi, in this case I would have it so the horizontal scroll views handle the scrolling. If you scroll up / down I would then pass the scroll value to the parent scroll view to do the vertical scrolling.

1 Like

I tried this using “verticalNormalizedPosition” without much luck. The horizontal scrollrects would pass either a 1 or 0, since their content was the same size as the scrollrect.

Was there a different method you meant I should try? Thanks.

I have the exact same problem, and am curious if anyone found a solution.

Yep, watching this thread if anyone can confirm a reliable solution to nesting scroll rects and getting smooth/fluid behavior.

This may be a complex enough situation to warrant just implementing the IDrag interfaces yourself.

Hi everyone! So I think I figured out a pretty easy, and I think elegant solution for touch screen devices for this (at least until unity supports this which I hope they will in the future!). I have a simple touch gesture script that sends out gesture events, so if you have a vertical scroll rect and a nested horizontal scroll rect like above, figure out in the OnGesture or whatever you have listening for the gesture if the user is swiping in a vertical motion. If they are, disable the ScrollRect script on the nested scroll rects. It works perfectly for anything that scrolls the opposite of the parent, at least. I tried using IDrag interfaces but there were too many issues I encountered, and I really wanted a way to not recreate what unity built with the scroll rects in the first place. If anyone has an even better solution, I’m all ears :slight_smile:

2 Likes

For me subclassing ScrollRect worked. I basically route the drag events to all parent ScrollRects in case a simple rule checked in OnBeginDrag is true. For my simple case I just wanted to route events to the parent in case a horizontal drag was being initiated but the scroll rect is a vertical one, or a vertical drag was being initiated and the scroll rect is a horizontal one. This is my subclass:

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

public class ScrollRectEx : ScrollRect {

    private bool routeToParent = false;


    /// <summary>
    /// Do action for all parents
    /// </summary>
    private void DoForParents<T>(Action<T> action) where T:IEventSystemHandler
    {
        Transform parent = transform.parent;
        while(parent != null) {
            foreach(var component in parent.GetComponents<Component>()) {
                if(component is T)
                    action((T)(IEventSystemHandler)component);
            }
            parent = parent.parent;
        }
    }

    /// <summary>
    /// Always route initialize potential drag event to parents
    /// </summary>
    public override void OnInitializePotentialDrag (PointerEventData eventData)
    {
        DoForParents<IInitializePotentialDragHandler>((parent) => { parent.OnInitializePotentialDrag(eventData); });
        base.OnInitializePotentialDrag (eventData);
    }

    /// <summary>
    /// Drag event
    /// </summary>
    public override void OnDrag (UnityEngine.EventSystems.PointerEventData eventData)
    {
        if(routeToParent)
            DoForParents<IDragHandler>((parent) => { parent.OnDrag(eventData); });
        else
            base.OnDrag (eventData);
    }

    /// <summary>
    /// Begin drag event
    /// </summary>
    public override void OnBeginDrag (UnityEngine.EventSystems.PointerEventData eventData)
    {
        if(!horizontal && Math.Abs (eventData.delta.x) > Math.Abs (eventData.delta.y))
            routeToParent = true;
        else if(!vertical && Math.Abs (eventData.delta.x) < Math.Abs (eventData.delta.y))
            routeToParent = true;
        else
            routeToParent = false;

        if(routeToParent)
            DoForParents<IBeginDragHandler>((parent) => { parent.OnBeginDrag(eventData); });
        else
            base.OnBeginDrag (eventData);
    }

    /// <summary>
    /// End drag event
    /// </summary>
    public override void OnEndDrag (UnityEngine.EventSystems.PointerEventData eventData)
    {
        if(routeToParent)
            DoForParents<IEndDragHandler>((parent) => { parent.OnEndDrag(eventData); });
        else
            base.OnEndDrag (eventData);
        routeToParent = false;
    }
}

Hope this helps! Cheers.

136 Likes

This does help! Way better than my solution. Thanks!

Nice solution @CaptainSchnittchen , fancy adding that to the UI Extensions (link in sig) bitbucket repo? (or let me know you are ok with it and I’ll add it :smile:)

1 Like

If you want to be able to use just scrolling with a touch pad, I find that the following method is needed to support that situation. Perhaps I am doing something wrong, but at least this works for me and might help someone else?

public override void OnScroll (PointerEventData eventData)
{
if (!horizontal && Math.Abs (eventData.scrollDelta.x) > Math.Abs (eventData.scrollDelta.y)) {
routeToParent = true;
} else if (!vertical && Math.Abs (eventData.scrollDelta.x) < Math.Abs (eventData.scrollDelta.y)) {
routeToParent = true;
} else
routeToParent = false;

if (routeToParent)
DoForParents ((parent) => {
parent.OnScroll (eventData); });
else
base.OnScroll (eventData);
}

Hi, Ssawyer! I have a similar task, but I couldn’t solve it. I need to make the scroll vertical menu. But it is necessary that each element of this menu could be also scrolled across (horizontal). At the same time on the screen at me only one element of the menu - the others outside visibility area. But at you, seemingly, everything well turned out. You completely could open hierarchy and show, what components at you are attached to each element? Do You could open hierarchy and show, what components at you are attached to each element? (For example, it is my hierarchy)…

Best regards!
Sergey.

I have also one little problem with rendering of text component. My Text component based on Canvas. And i have simple 3D-object near camera. And… all element of menu rendered normal, but title text rendered above 3D-object (Text component located on same layer and z-position? as his parent objects).

Oh man this helped me out A LOT, expect that after I scroll it doesn’t lerp. Is there something blocking it from using Inertia?

1 Like

[quote=“CaptainSchnittchen, post:10, topic: 550540, username:CaptainSchnittchen”]
For me subclassing ScrollRect worked. I basically route the drag events to all parent ScrollRects in case a simple rule checked in OnBeginDrag is true. For my simple case I just wanted to route events to the parent in case a horizontal drag was being initiated but the scroll rect is a vertical one, or a vertical drag was being initiated and the scroll rect is a horizontal one. This is my subclass: … Hope this helps! Cheers.
[/quote]

THAT
IS
FUCKING
IMPRESSIVE.

Merely your
DoForParents
class is incredibly beautiful.
Wow. Holy crap.

CaptainSchnittchen - Holy F-ING Cow! YOU KICK ASS!
This was pretty much a deal breaker in my app because it seriously hinders the user experience.
Thanks for the share.

CaptainSchnittchen - A huge thank you!