Nested ScrollView

Hi Corona Community,

I am developing an app, and for one of the scenes I would like to nest a horizontal ScrollView within a vertical ScrollView.

Now according to the official documentation, there should be no problems nesting ScrollViews, as long as the nesting count doesn’t exceed 3. Notwithstanding, I’ve been encountering problems, and I’m only trying to nest one ScrollView into another (for a nesting count of 2).

Please find the code below, a boiled-down version of what I am trying to do. The code complies without any errors, so you can plug it into your favorite platform and see for yourself.
 

display.setStatusBar(display.HiddenStatusBar) local widget = require("widget") local CX, CY = display.contentCenterX, display.contentCenterY local title local horizontal1Text, horizontal2Text, horizontal3Text local horizontal1, horizontal2, horizontal3 local vertical1Text, vertical2Text, vertical3Text local vertical1, vertical2, vertical3 local scrollView, verticalScrollView, horizontalScrollView title = display.newText("Scroll View Sucks", CX, 100, native.systemFont, 60) title:setFillColor(1) vertical1Text = display.newText("Ver Obj 1", CX, 200) vertical1Text:setFillColor(1) vertical1 = display.newRect(CX, 300, 100, 100) vertical1:setFillColor(1,0,1) vertical2Text = display.newText("Ver Obj 2", CX, 500) vertical2Text:setFillColor(1) vertical2 = display.newCircle(CX, 600, 55) vertical2:setFillColor(0,0,1) horizontal1Text = display.newText("Hrz Obj 1", 100, 800) horizontal1Text:setFillColor(1) horizontal1 = display.newRect(100, 900, 100, 100) horizontal1:setFillColor(1,1,0) horizontal2Text = display.newText("Hrz Obj 2", 400, 800) horizontal2Text:setFillColor(1) horizontal2 = display.newCircle(400, 900, 55) horizontal2:setFillColor(0,1,1) horizontal3Text = display.newText("Hrz Obj 3", 700, 800) horizontal3Text:setFillColor(1) horizontal3 = display.newRect(700, 900, 100, 100) horizontal3:setFillColor(1,0,0) vertical3Text = display.newText("Ver Obj 3", CX, 1100) vertical3Text:setFillColor(1) vertical3 = display.newCircle(CX, 1200, 55) vertical3:setFillColor(0,1,0) function initHorizontalScrollView() horizontalScrollView = widget.newScrollView { left = 0, top = 0, width = display.contentWidth, height = display.contentHeight, horizontalScrollDisabled = false, verticalScrollDisabled = true, hideBackground = true } horizontalScrollView:insert(title) horizontalScrollView:insert(horizontal1Text); horizontalScrollView:insert(horizontal1) horizontalScrollView:insert(horizontal2Text); horizontalScrollView:insert(horizontal2) horizontalScrollView:insert(horizontal3Text); horizontalScrollView:insert(horizontal3) end function initVerticalScrollView() verticalScrollView = widget.newScrollView { left = 0, top = 0, width = display.contentWidth, height = display.contentHeight, horizontalScrollDisabled = true, verticalScrollDisabled = false, hideBackground = true } verticalScrollView:insert(title) verticalScrollView:insert(vertical1Text); verticalScrollView:insert(vertical1) verticalScrollView:insert(vertical2Text); verticalScrollView:insert(vertical2) verticalScrollView:insert(vertical3Text); verticalScrollView:insert(vertical3) --verticalScrollView:insert(horizontalScrollView) end function initScrollView() scrollView = widget.newScrollView { left = 0, top = 0, width = display.contentWidth, height = display.contentHeight, horizontalScrollDisabled = false, verticalScrollDisabled = false, hideBackground = true } ---[[scrollView:insert(title) scrollView:insert(vertical1Text); scrollView:insert(vertical1) scrollView:insert(vertical2Text); scrollView:insert(vertical2) scrollView:insert(vertical3Text); scrollView:insert(vertical3) scrollView:insert(horizontal1Text); scrollView:insert(horizontal1) scrollView:insert(horizontal2Text); scrollView:insert(horizontal2) scrollView:insert(horizontal3Text); scrollView:insert(horizontal3) --]] --[[scrollView:insert(horizontalScrollView) scrollView:insert(verticalScrollView) --]] end initHorizontalScrollView() initVerticalScrollView() --initScrollView()

As I said above, the goal is to place all the “horizontal” objects into a horizontal ScrollView, and then put everything into a larger vertical ScrollView.

This sounds like a pretty straight-forward task.

However, when you call initVerticalScrollView( ) after calling initHorizontalScrollView( ), the previously created horizontal scroll view is destroyed (i.e. you can’t pan horizontally anymore). In a similar fashion, the vertical scroll view is destroyed when the order of functions being called is reversed.

This has left me thinking that no more than 1 scroll view can exist at any time.

So I tried creating a scroll view that allows both horizontal and vertical scrolling, and stuffed all the objects in it. Comment out initVerticalScrollView( ) and initHorizontalScrollView( ), and uncomment initScrollView( ) to see the effect. As you can see, when you try to pan horizontally, all the contents, including objects that are not intended to do so, pan too!

I also tried adding both verticalScrollView and horizontalScrollView to a scrollView, but then nothing scrolls anymore.

So, basically, nothing has worked so far and I am out of ideas. I think it is pretty clear I am trying to achieve here, and am surprised to learn that Corona doesn’t support this not-so-complicated task.

I am being silly here and missing something very obvious, or is what am I trying to do impossible? If you believe the former is the case, please tell me where I went wrong and possibly include code as well.

Thanks a lot in advance :slight_smile:

At the core, a scrollView is nothing more than a display.newGroup() (well I think it’s a display.newContainer() which is just a self-masked display.newGroup()). As such display objects can only be in one group at a time. This seems to only affect your “title” object. 

Next, you’re putting a full screen horizontal only over top of a full screen vertical only. Usually people nesting scrollViews put smaller partial screen scrollViews inside of a larger full screen scrollView. If both of your scrollViews are taking up the whole screen, why not just one one scrollView?

Regardless you need to hand focus to the bottom scrollView. See: https://docs.coronalabs.com/api/type/ScrollViewWidget/takeFocus.html

You will have to listen for scroll events and pass non-horizontal movement actions to the bottom scrollView.  Still you may be better off with a single scrollView that moves in multiple directions or make the horiztonal scrollViews only the height of the content in them.

Rob

@Rob Miracle

Next, you’re putting a full screen horizontal only over top of a full screen vertical only. 

Perhaps it was my poor wording, but I do not want a “full screen horizontal only”. I want the vertical scroll view to cover the whole screen, but I do NOT what the horizontal scroll view to be “full screen”. I only want it to apply the objects labeled “horizontal”.

 

Still you may be better off with a single scrollView that moves in multiple directions

As I mentioned in the post, when I create a “scrollView that moves in multiple directions”, all the objects move when I scroll horizontally. This is NOT the behavior I want. View the attached image to see what happens when I do what you suggested. I what only the objects labeled “horizontal” to scroll, and the objects labeled “vertical” to stay in place.

 

or make the horiztonal scrollViews only the height of the content in them.

This also doesn’t work, for as soon as I introduce a second scroll view, the original one stops functioning.

If anyone has a solution, or what might be a solution, I am all ears!

Thanks,

KC

 horizontalScrollView = widget.newScrollView { left = 0, top = 0, width = display.contentWidth, --\<---- makes the scrollView full screen width height = display.contentHeight, --\<---- makes the scrollView full screen height horizontalScrollDisabled = false, verticalScrollDisabled = true, hideBackground = true }

I get what you’re saying. You are trying to use the horizontal scrollView to mimic rows in a table. Each horizontal band needs to be it’s own horizontal only scrollView who’s height is just enough to hold the content of that row and you will need multiple of them, one for each row you want to scroll.

If your horizontal scrollling scrollViews are all stacked neatly on top of each other, you’re still going to have to pass focus to the bottom full screen vertically scrolling scrollView.

Rob

@Rob Miracle
Thanks for your reply.

By “makes the scrollView full screen width/height”, do you mean “display.actualContentWidth/Height”? I tried that, but that didn’t fix the problem.

From what I gather, you think it is :POSSIBLE: to achieve what I am saying, correct?

I provided code in my original post. Could you be so kind as to modify some of the code and post it to get me started?
You mentioned passing focus to the vertical scrollview. How exactly do I go about doing this? 

Thanks,

KC

I really wish I could draw. Visuals would so help.  Think of your screen like a picture frame, say 8x10". Then you want to have a vertical scrollView that’s also an 8x10 frame with a big solid picture that can move up and down in the frame. That’s fine, you can reach through the front frame, touch the back frame and scroll the image.

Now you want to insert another 8x10 picture frame in between the front frame (your whole screen) and the back scrollView, but the one you’re putting in the middle is also an 8x10 frame filled with a solid picture that you can only scroll sideways. How do you scroll the back picture if you can’t get through it?

It’s impossible to get to the back scrollView because you’re blocked by the first scrollView.  So you have a choice:

  1. a single scrollView that can scroll both directions.

  2. a vertical full screen scrollView in the background that takes up the full screen. and then have one or more horizontal scrolling scrollViews that only take up a small amount of vertical space. Think of bands.

Rob

Okay, so I tried what Rob Miracle said, but neither worked. It is possible I am doing something incorrectly, so that is not to say it cannot be done, but at least I couldn’t figure out how to make the scrollview behave in my desired way.

However, I did find (my own) solution!!

The Corona community has helped me many times, so in return, I will share how I did it (there may be other ways to do it). This is my way of, albeit rather small, giving back to the community.

So in short my solution was not to use scrollviews at all! Well, not exactly at all, but I did not use nested scrollviews. In fact, as far as I am concerned, nested scrollviews are NOT POSSIBLE in Corona. Rob Miracle suggested using a scrollview that pans both vertically and horizontally, but the behavior was chaotic, and was not that of what I wanted to achieve.

So I used ONLY ONE scrollview that scrolls VERTICALLY ONLY. No nested scrollviews or multi-directionally scrolling shenanigans.
For the horizontal portion, first I determined where the “band” of the horizontally scrolling objects lies. You can calculate this by using event.y for mouse clicks (or touches) and the position of the vertical scrollview. This can be done via scrollview:getContentPosition( )

Once you determine where the “band” lies, you can simply move the objects by allowing dragging, whenever the initial touch is in the “band”. There are various tutorials and documentation for how to drag on object. I simply used the one I found in the Corona SDK Sample Code.

Using event.x and event.y for dragging mimics the motion of a scrollview while never having have to use it, and circumvents the whole “who has the focus” conundrum when attempting nested scrollviews.

With this approach, I got exactly the behavior I was striving for.

Again, there may be other solutions out there, but none of them worked for me, and I think this is a nice and simple solution to a rather complicated problem.

KC

On a different note, is there a way to mark this topic as “Solved” without making any particular post the best answer?

I just simply want to mark the topic as solved, and not mark any post as “the best answer”, which would imply the rest weren’t as helpful…

At the core, a scrollView is nothing more than a display.newGroup() (well I think it’s a display.newContainer() which is just a self-masked display.newGroup()). As such display objects can only be in one group at a time. This seems to only affect your “title” object. 

Next, you’re putting a full screen horizontal only over top of a full screen vertical only. Usually people nesting scrollViews put smaller partial screen scrollViews inside of a larger full screen scrollView. If both of your scrollViews are taking up the whole screen, why not just one one scrollView?

Regardless you need to hand focus to the bottom scrollView. See: https://docs.coronalabs.com/api/type/ScrollViewWidget/takeFocus.html

You will have to listen for scroll events and pass non-horizontal movement actions to the bottom scrollView.  Still you may be better off with a single scrollView that moves in multiple directions or make the horiztonal scrollViews only the height of the content in them.

Rob

@Rob Miracle

Next, you’re putting a full screen horizontal only over top of a full screen vertical only. 

Perhaps it was my poor wording, but I do not want a “full screen horizontal only”. I want the vertical scroll view to cover the whole screen, but I do NOT what the horizontal scroll view to be “full screen”. I only want it to apply the objects labeled “horizontal”.

 

Still you may be better off with a single scrollView that moves in multiple directions

As I mentioned in the post, when I create a “scrollView that moves in multiple directions”, all the objects move when I scroll horizontally. This is NOT the behavior I want. View the attached image to see what happens when I do what you suggested. I what only the objects labeled “horizontal” to scroll, and the objects labeled “vertical” to stay in place.

 

or make the horiztonal scrollViews only the height of the content in them.

This also doesn’t work, for as soon as I introduce a second scroll view, the original one stops functioning.

If anyone has a solution, or what might be a solution, I am all ears!

Thanks,

KC

 horizontalScrollView = widget.newScrollView { left = 0, top = 0, width = display.contentWidth, --\<---- makes the scrollView full screen width height = display.contentHeight, --\<---- makes the scrollView full screen height horizontalScrollDisabled = false, verticalScrollDisabled = true, hideBackground = true }

I get what you’re saying. You are trying to use the horizontal scrollView to mimic rows in a table. Each horizontal band needs to be it’s own horizontal only scrollView who’s height is just enough to hold the content of that row and you will need multiple of them, one for each row you want to scroll.

If your horizontal scrollling scrollViews are all stacked neatly on top of each other, you’re still going to have to pass focus to the bottom full screen vertically scrolling scrollView.

Rob

@Rob Miracle
Thanks for your reply.

By “makes the scrollView full screen width/height”, do you mean “display.actualContentWidth/Height”? I tried that, but that didn’t fix the problem.

From what I gather, you think it is :POSSIBLE: to achieve what I am saying, correct?

I provided code in my original post. Could you be so kind as to modify some of the code and post it to get me started?
You mentioned passing focus to the vertical scrollview. How exactly do I go about doing this? 

Thanks,

KC

I really wish I could draw. Visuals would so help.  Think of your screen like a picture frame, say 8x10". Then you want to have a vertical scrollView that’s also an 8x10 frame with a big solid picture that can move up and down in the frame. That’s fine, you can reach through the front frame, touch the back frame and scroll the image.

Now you want to insert another 8x10 picture frame in between the front frame (your whole screen) and the back scrollView, but the one you’re putting in the middle is also an 8x10 frame filled with a solid picture that you can only scroll sideways. How do you scroll the back picture if you can’t get through it?

It’s impossible to get to the back scrollView because you’re blocked by the first scrollView.  So you have a choice:

  1. a single scrollView that can scroll both directions.

  2. a vertical full screen scrollView in the background that takes up the full screen. and then have one or more horizontal scrolling scrollViews that only take up a small amount of vertical space. Think of bands.

Rob

Okay, so I tried what Rob Miracle said, but neither worked. It is possible I am doing something incorrectly, so that is not to say it cannot be done, but at least I couldn’t figure out how to make the scrollview behave in my desired way.

However, I did find (my own) solution!!

The Corona community has helped me many times, so in return, I will share how I did it (there may be other ways to do it). This is my way of, albeit rather small, giving back to the community.

So in short my solution was not to use scrollviews at all! Well, not exactly at all, but I did not use nested scrollviews. In fact, as far as I am concerned, nested scrollviews are NOT POSSIBLE in Corona. Rob Miracle suggested using a scrollview that pans both vertically and horizontally, but the behavior was chaotic, and was not that of what I wanted to achieve.

So I used ONLY ONE scrollview that scrolls VERTICALLY ONLY. No nested scrollviews or multi-directionally scrolling shenanigans.
For the horizontal portion, first I determined where the “band” of the horizontally scrolling objects lies. You can calculate this by using event.y for mouse clicks (or touches) and the position of the vertical scrollview. This can be done via scrollview:getContentPosition( )

Once you determine where the “band” lies, you can simply move the objects by allowing dragging, whenever the initial touch is in the “band”. There are various tutorials and documentation for how to drag on object. I simply used the one I found in the Corona SDK Sample Code.

Using event.x and event.y for dragging mimics the motion of a scrollview while never having have to use it, and circumvents the whole “who has the focus” conundrum when attempting nested scrollviews.

With this approach, I got exactly the behavior I was striving for.

Again, there may be other solutions out there, but none of them worked for me, and I think this is a nice and simple solution to a rather complicated problem.

KC

On a different note, is there a way to mark this topic as “Solved” without making any particular post the best answer?

I just simply want to mark the topic as solved, and not mark any post as “the best answer”, which would imply the rest weren’t as helpful…