Possible bug? Tableview index not updating after deleteRows

I’m having an issue with a Tableview where the ID of the rows doesn’t get updated after a row has been deleted with deleteRows. I’ve tried to reload the table but it does not help. It seems someone else had the issue a couple of years ago and it doesn’t seem to be fixed?

Below you can find a full working example showing the problem. Sorry I couldn’t make it smaller.

local composer = require( "composer" ) local scene = composer.newScene() local widget = require "widget" local globalData = require( "globalData" ) local tableView local deleteButton local function confirmDeleteFile( event )     if ( event.action == "clicked" ) then       local i = event.index       if ( i == 1 ) then         local result, reason = os.remove( system.pathForFile( globalData.fileTapped, system.DocumentsDirectory ) )          tableView:deleteRows( { globalData.rowIndexTapped } )          tableView:reloadData()       end       return true -- indicates successful touch     end end local function onDeleteButtonRelease(event)   local alert = native.showAlert( "Delete saved game", "Are you sure you want to delete this game?", { "Yes", "No, cancel" }, confirmDeleteFile )   return true -- indicates successful touch end local function onRowTouch(event)     print("onRowTouch (event.target.index): "..event.target.index)     local row = event.target        if ( event.phase == "release" or event.phase == "tap" ) then       globalData.fileTapped = event.target.params.file       globalData.rowIndexTapped = event.target.index              deleteButton:addEventListener("tap", onDeleteButtonRelease)              for i = 1, tableView:getNumRows() do             if i == row.index then           tableView.\_view.\_rows[i].\_rowColor.default = { 1, 100/255, 100/255 }         else           local indexVisibleLine = tableView:getRowAtIndex( i ) -- This makes sure the line is visible. If not visible, \_view.\_rows[i] will crash           if ( indexVisibleLine ) then             tableView.\_view.\_rows[i].\_rowColor.default = { 232/255, 232/255, 232/255 }           end         end       end       tableView:reloadData()         return true -- indicates successful touch           end end function scene:create( event ) local sceneGroup = self.view   local function onRowRender( event )           local row = event.row              local rowTitle = display.newText( row, event.row.params.file, 0, 0, nil, 100 )       rowTitle:setFillColor( 0 )           rowTitle.anchorX = 0       rowTitle.x = 0       rowTitle.y = 56 -- half of 112 (current rowHeight)   end      tableView = widget.newTableView(       {         left = 0,         top = 0,         height = display.contentHeight-400,         width = display.contentWidth,         onRowRender = onRowRender,         onRowTouch = onRowTouch,         listener = scrollListener       }   )   local lfs = require ( "lfs" )   local path = system.pathForFile( "", system.DocumentsDirectory )   local savedGames = {}   for file in lfs.dir ( path ) do       if string.find( file, ".db" ) then         table.insert( savedGames, file )         tableView:insertRow{           rowHeight = 112,           params = {file=file},  -- Include custom data in the row           rowColor = {                    default = { 255, 255, 255},                   over = { 255, 0, 0},           },         }       end   end      deleteButton = widget.newButton(     {         width = 200,         height = 100,         defaultFile = "button.png",         overFile = "button-over.png",         fontSize= 60,         labelColor = { default={255}, over={128} }     }   )    deleteButton.x = display.actualContentWidth/1.5 deleteButton.y = display.actualContentHeight\*0.9     deleteButton:setLabel( "Delete" ) sceneGroup:insert( deleteButton ) sceneGroup:insert( tableView ) end function scene:destroy( event ) local sceneGroup = self.view deleteButton:removeSelf() -- widgets must be manually removed deleteButton = nil        tableView:removeSelf()   tableView = nil end scene:addEventListener( "create", scene ) scene:addEventListener( "destroy", scene ) return scene

Tapping on each row after a deletion will print:

onRowTouch (event.target.index): 1

onRowTouch (event.target.index): 2

onRowTouch (event.target.index): 4

onRowTouch (event.target.index): 5

onRowTouch (event.target.index): 6

If someone can confirm this is a bug I can file a bug report.

I am wondering if it is a bug or design, as I can see cases where recalculating the index would cause some issues.

Let’s say you populate a tableview and the index is used to refer to positions in an array (a common scenario).

When you then delete a row, you dont want the index to be recalculated, or else your array is gonna get mixed up the next time you access a row.

What you need to do in that case is to requery and rebuild the tableview, a kind of “refresh”.

tableview:reloadData() will not work when items need to be added or removed.

Hope that makes sense somehow  :smiley:

But that would mean that you cannot delete multiple rows (one by one) on a given TableView without making such adjustments…

Which part would the “requery and rebuild the tableview” be? tableView:insertRow only? I’ve tried a couple of things but same result.

oh yes you can.

in the OnRowTouch function you use event.target, which points to the row itself, instead of event.target.index which is just the stored index from when the tableview was created.

i think my requery and rebuild statement is colored by how my app handle it.

i have a query function that pulls data from a sql server and stores it in an array.

after each query, i delete all rows and run a tableView:insertRow loop to repopulate the tableView.

how you get the source data used to populate the tableView may differ but the process should be close to identical.

I actually event.target.index in the loop when I do  if i == row.index then with row being  event.target

I then store that in  globalData.rowIndexTapped

So I’ve changed my code to the following but the behavior hasn’t changed. Can you see anything wrong?

local confirmDeleteFile local onDeleteButtonRelease local function RefreshTable ()  print("refreshing table")   local lfs = require ( "lfs" )   local path = system.pathForFile( "", system.DocumentsDirectory )   local savedGames = {}   for file in lfs.dir ( path ) do       if string.find( file, ".db" ) then         table.insert( savedGames, file )         tableView:insertRow{           rowHeight = 112,           params = {file=file},  -- Include custom data in the row           rowColor = {                    default = { 255, 255, 255},                   over = { 255, 0, 0},           },         }       end   end   tableView:reloadData() end confirmDeleteFile = function ( event )     if ( event.action == "clicked" ) then       print("cliked confirmDeleteFile")       local i = event.index       if ( i == 1 ) then         local result, reason = os.remove( system.pathForFile( globalData.fileTapped, system.DocumentsDirectory ) )          tableView:deleteRows( { globalData.rowIndexTapped } )          tableView:deleteAllRows()          deleteButton:removeEventListener("tap", onDeleteButtonRelease)          RefreshTable ()       end       print("done")       return true -- indicates successful touch     end end onDeleteButtonRelease = function (event)   local alert = native.showAlert( "Delete saved game", "Are you sure you want to delete this game?", { "Yes", "No, cancel" }, confirmDeleteFile )   return true -- indicates successful touch end

The console prints:

refreshing table

onRowTouch (event.target.index): 5  – tap on row 5

cliked confirmDeleteFile

refreshing table

done

onRowTouch (event.target.index): 4  – tap on row 4

onRowTouch (event.target.index): 6  – tap on row 5

we code very different and it seems to be but a snippet, but I just did a test in my app where i printed event.target.index on clicking row 3, giving index = 3.

then i deleted row 3, did a sql requery and repopulated the tableview.

the new row 3 (previously row 4) also shows index 3 on print, so you must be doing something strange. I dont get that index behaviour.

i strongly recommend checking with the online corona documentation if you havent already, which is well made and up to date.

oh that works now, not sure what I actually changed. Thanks!

glad you figurered it out  :smiley:

I am wondering if it is a bug or design, as I can see cases where recalculating the index would cause some issues.

Let’s say you populate a tableview and the index is used to refer to positions in an array (a common scenario).

When you then delete a row, you dont want the index to be recalculated, or else your array is gonna get mixed up the next time you access a row.

What you need to do in that case is to requery and rebuild the tableview, a kind of “refresh”.

tableview:reloadData() will not work when items need to be added or removed.

Hope that makes sense somehow  :smiley:

But that would mean that you cannot delete multiple rows (one by one) on a given TableView without making such adjustments…

Which part would the “requery and rebuild the tableview” be? tableView:insertRow only? I’ve tried a couple of things but same result.

oh yes you can.

in the OnRowTouch function you use event.target, which points to the row itself, instead of event.target.index which is just the stored index from when the tableview was created.

i think my requery and rebuild statement is colored by how my app handle it.

i have a query function that pulls data from a sql server and stores it in an array.

after each query, i delete all rows and run a tableView:insertRow loop to repopulate the tableView.

how you get the source data used to populate the tableView may differ but the process should be close to identical.

I actually event.target.index in the loop when I do  if i == row.index then with row being  event.target

I then store that in  globalData.rowIndexTapped

So I’ve changed my code to the following but the behavior hasn’t changed. Can you see anything wrong?

local confirmDeleteFile local onDeleteButtonRelease local function RefreshTable ()  print("refreshing table")   local lfs = require ( "lfs" )   local path = system.pathForFile( "", system.DocumentsDirectory )   local savedGames = {}   for file in lfs.dir ( path ) do       if string.find( file, ".db" ) then         table.insert( savedGames, file )         tableView:insertRow{           rowHeight = 112,           params = {file=file},  -- Include custom data in the row           rowColor = {                    default = { 255, 255, 255},                   over = { 255, 0, 0},           },         }       end   end   tableView:reloadData() end confirmDeleteFile = function ( event )     if ( event.action == "clicked" ) then       print("cliked confirmDeleteFile")       local i = event.index       if ( i == 1 ) then         local result, reason = os.remove( system.pathForFile( globalData.fileTapped, system.DocumentsDirectory ) )          tableView:deleteRows( { globalData.rowIndexTapped } )          tableView:deleteAllRows()          deleteButton:removeEventListener("tap", onDeleteButtonRelease)          RefreshTable ()       end       print("done")       return true -- indicates successful touch     end end onDeleteButtonRelease = function (event)   local alert = native.showAlert( "Delete saved game", "Are you sure you want to delete this game?", { "Yes", "No, cancel" }, confirmDeleteFile )   return true -- indicates successful touch end

The console prints:

refreshing table

onRowTouch (event.target.index): 5  – tap on row 5

cliked confirmDeleteFile

refreshing table

done

onRowTouch (event.target.index): 4  – tap on row 4

onRowTouch (event.target.index): 6  – tap on row 5

we code very different and it seems to be but a snippet, but I just did a test in my app where i printed event.target.index on clicking row 3, giving index = 3.

then i deleted row 3, did a sql requery and repopulated the tableview.

the new row 3 (previously row 4) also shows index 3 on print, so you must be doing something strange. I dont get that index behaviour.

i strongly recommend checking with the online corona documentation if you havent already, which is well made and up to date.

oh that works now, not sure what I actually changed. Thanks!

glad you figurered it out  :smiley: