How do I scroll-to-bottom in a TableView?

[Here’s the short question: Can I easily scroll to the bottom of a TableView? Or, can I use scrollToY so that it scrolls relatively from the current point, rather than from 0? For more explanation, read below.]

I am trying to create a TableView that scrolls to the bottom after I insert a row at the end, something like a typical text messaging app, where the new message I enter will scroll everything up a little bit to make room for the new message, which shows up at the bottom end of the view.

This proved rather tricky, as the only options I could see were to use scrollToIndex (which puts the new message at the top, not at the bottom, and it’s not possible to calculate which message to put at the top because each row is a different height) or to use scrollToY.

I found a field called tableView._view._scrollHeight which helps me do this. Since then I figured out that I could probably get the final position of the last row by checking the onRowRender field, although that’s a bit of a hassle. Anyway, the below code does “work”, except for one thing …

[lua]
local offset = -tableView._view._scrollHeight+tableHeight – tableHeight is the on-screen height of the TableView

tableView:scrollToY({y=offset})
[/lua]

… The problem is that scrollToY seems to always scroll from the very top to the offset point. So, if I have 1000 messages in the TableView, it will scroll through all those 1000 messages or so to get to the point near the bottom, whereas of course I would just want it to scroll up by the few pixels necessary to get to the bottom (normally, it should scroll the height of the new row, but maybe more if the user happens to be elsewhere in the view).

So, how do I scroll just a certain amount, rather than through the whole table? Or is there some easier way to just scroll to bottom in a TableView?

This is how I did it:

When inserting rows keep track of the height of the contents and row count:

[lua]contentsHeight = rowHeight + contentsHeight

rowCount = rowCount + 1[/lua]

Scroll to bottom function:

[lua]

local function scrollToBottom(animate)

    

    – Check if scrolling is needed

    if contentsHeight < tableHeight then return end

    

    local scrollTime = 0; if animate then scrollTime = 500 end

    local scrollTo   = -(contentsHeight) + tableHeight - rowCount – Seems you need 1 extra px for each row, hence rowCount.

    tableView:scrollToY{ y = scrollTo, time = scrollTime}

end

[/lua]

Thanks for the reply, Jon, but doesn’t this suffer from the same problem? I.e., in the event that the control does need to scroll, doesn’t it scroll all the way from the top?

(Try setting scrollTime to something like 3000, and you’ll see all the stuff starting from the very top as we go towards the bottom. Unless this is something that was changed in G2.0, which we haven’t migrated to yet.)

Hmm not sure I’m following. It just scrolls relative from where the tableview is at that time.

Is the problem that when you open the tableview you see the top rows and thus it starts scrolling down from there? If so you can use time = 0 in the scroll function before displaying the table (like in willEnterScene perhaps)?

Found a bug in Graphics 2 that scrollToY doesn’t work in willEnterScene anymore but confirmed works fine in pre g2 version.

The problem is that *each* time we add a row and scroll to the row’s position (not just on open), the view starts at the top then scrolls all the way down. We could use time=0 of course, but then the display will look like it’s jumping unnaturally. The behavior you’re describing (scrolling relative to its current position) is what we are going for, but it’s not doing that on my system.

Maybe this is only a problem in the Mac simulator. I haven’t tested yet on a device. I’m using build #2013.1260 on OS X 10.9 Mavericks. I’ve actually been having some strange behavior in Mavericks with Corona (as well as other software), so perhaps that’s it. Could you share which version/system you are running on?

I’m on mac and this has been working for months with numerous dailies with no problem.

When I add a line to my chat, I use time = 500 to get a nice small scroll, so its definitely not behaving like you are describing.

If you make a minimized runable demo I can test it out for you.

Sincere apologies … both methods work just fine. I didn’t check another part of the code which was actually recreating the tableView on each “send” to maintain consistency with an asynchronously changing DB. So, of course it was resetting to the top. 

Thanks for your insistence, Jon. You were right all along.

Np, glad it worked for you.

This is how I did it:

When inserting rows keep track of the height of the contents and row count:

[lua]contentsHeight = rowHeight + contentsHeight

rowCount = rowCount + 1[/lua]

Scroll to bottom function:

[lua]

local function scrollToBottom(animate)

    

    – Check if scrolling is needed

    if contentsHeight < tableHeight then return end

    

    local scrollTime = 0; if animate then scrollTime = 500 end

    local scrollTo   = -(contentsHeight) + tableHeight - rowCount – Seems you need 1 extra px for each row, hence rowCount.

    tableView:scrollToY{ y = scrollTo, time = scrollTime}

end

[/lua]

Thanks for the reply, Jon, but doesn’t this suffer from the same problem? I.e., in the event that the control does need to scroll, doesn’t it scroll all the way from the top?

(Try setting scrollTime to something like 3000, and you’ll see all the stuff starting from the very top as we go towards the bottom. Unless this is something that was changed in G2.0, which we haven’t migrated to yet.)

Hmm not sure I’m following. It just scrolls relative from where the tableview is at that time.

Is the problem that when you open the tableview you see the top rows and thus it starts scrolling down from there? If so you can use time = 0 in the scroll function before displaying the table (like in willEnterScene perhaps)?

Found a bug in Graphics 2 that scrollToY doesn’t work in willEnterScene anymore but confirmed works fine in pre g2 version.

The problem is that *each* time we add a row and scroll to the row’s position (not just on open), the view starts at the top then scrolls all the way down. We could use time=0 of course, but then the display will look like it’s jumping unnaturally. The behavior you’re describing (scrolling relative to its current position) is what we are going for, but it’s not doing that on my system.

Maybe this is only a problem in the Mac simulator. I haven’t tested yet on a device. I’m using build #2013.1260 on OS X 10.9 Mavericks. I’ve actually been having some strange behavior in Mavericks with Corona (as well as other software), so perhaps that’s it. Could you share which version/system you are running on?

I’m on mac and this has been working for months with numerous dailies with no problem.

When I add a line to my chat, I use time = 500 to get a nice small scroll, so its definitely not behaving like you are describing.

If you make a minimized runable demo I can test it out for you.

Sincere apologies … both methods work just fine. I didn’t check another part of the code which was actually recreating the tableView on each “send” to maintain consistency with an asynchronously changing DB. So, of course it was resetting to the top. 

Thanks for your insistence, Jon. You were right all along.

Np, glad it worked for you.