This widget requires Corona build 2011.646 or higher. Some features require build 2012.721 or later.

tableViews allow you to create scrolling lists. As of widget version 0.2 (beta), you will now control 100% of the rendering of the individual rows (to include category rows). This allows for much greater flexibility, however, it also requires a bit more work getting them set up.
Additionally, it is a known issue that scroll bars are no longer present in this version. Please be patient as we are working on restoring scrollbars in a future update to this widget.
If you want a tableView that does not extend to the full height of the device's screen, you need to create a bitmap mask that corresponds to the width/height of your tableView. Read this tutorial to learn how to properly create a bitmap mask specifically for scrollViews and tableViews.
widget.newTableView( [options] )
local widget = require "widget" local listOptions = { top = display.statusBarHeight, height = 410, maskFile = "mask-410.png" } local list = widget.newTableView( listOptions ) -- onEvent listener for the tableView local function onRowTouch( event ) local row = event.target local rowGroup = event.view if event.phase == "press" then if not row.isCategory then rowGroup.alpha = 0.5; end elseif event.phase == "swipeLeft" then print( "Swiped left." ) elseif event.phase == "swipeRight" then print( "Swiped right." ) elseif event.phase == "release" then if not row.isCategory then -- reRender property tells row to refresh if still onScreen when content moves row.reRender = true print( "You touched row #" .. event.index ) end end return true end -- onRender listener for the tableView local function onRowRender( event ) local row = event.target local rowGroup = event.view local text = display.newRetinaText( "Row #" .. event.index, 12, 0, "Helvetica-Bold", 18 ) text:setReferencePoint( display.CenterLeftReferencePoint ) text.y = row.height * 0.5 if not row.isCategory then text.x = 15 text:setTextColor( 0 ) end -- must insert everything into event.view: rowGroup:insert( text ) end -- Create 100 rows, and two categories to the tableView: for i=1,100 do local rowHeight, rowColor, lineColor, isCategory -- make the 25th item a category if i == 25 then isCategory = true; rowHeight = 24; rowColor={ 70, 70, 130, 255 }; lineColor={0,0,0,255} end -- make the 45th item a category as well if i == 45 then isCategory = true; rowHeight = 24; rowColor={ 70, 70, 130, 255 }; lineColor={0,0,0,255} end -- function below is responsible for creating the row list:insertRow{ onEvent=onRowTouch, onRender=onRowRender, height=rowHeight, isCategory=isCategory, rowColor=rowColor, lineColor=lineColor } end -- delete the tenth row in the tableView list:deleteRow( 10 ) -- can be an index number or an actual row table (from within an event)
widget.newTableView() takes a single argument, options, which is a Lua table that accepts specific parameters. Please read over Properties and Methods section before using this widget, and study the example above.
bgColor
table. This table should have the red, green, blue, and alpha channels for the color of the rectangle to be placed behind the tableView. Default is white:
{ 255, 255, 255, 255 }
hideBackground
[Requires build 2012.721 or later] boolean (true/false). If you set this parameter to false, the background of the tableView widget will be hidden (but still receive touches).
left, top
numbers. The left and top coordinate in which the tableView will be placed upon creation. The default value for both is 0.
width, height
numbers. The width represents the maximum width that rows will be rendered. The height represents the total height of the tableView. The default values are the full width and height of the screen. Remember, if you set a height, you must provide a matching bitmap mask so the tableView is cropped correctly. Read this tutorial to learn how to create a bitmap mask for use with tableViews and scrollViews.
topPadding, bottomPadding
numbers. These are numbers that represent the number of pixels from the top and bottom of the tableView in which rows will stop when you reach the top/bottom of the list. The default value for both is 0.
renderThresh
number. This is the pixel amount to the top and bottom of the tableView in which rows are rendered and de-rendered. If for whatever reason you're able to see the rows being de-rendered, you should increase this value from the default of 150. In most cases, you should never have to touch this.
friction
number. This determines how fast the rows travel when they are flicked up or down. The default value is 0.935, which should be sufficient for most cases. In previous versions of this widget, you were not able to set this property, but we realized there may be times you want the "flick speed" to be faster or slower.
maxVelocity
[Requires build 2012.721 or later] number. Use this to limit the maximum scrolling speed of the TableView widget. The default value is 10.
maskFile
string. If you set a custom height, you need to specify the maskFile parameter, which is the filename of the bitmap mask which will be used on your tableView. Read this tutorial to learn how to create a bitmap mask for use with tableViews and scrollViews.
view
NOTE: As of build 2012.721, there is no longer a .view property. The widget IS the display object.. If you need to do something with the widget's display object (such as place the button into a display group), you need to do so using the object's .view property. Example:
-- Only applies to builds prior to 2012.721: -- WRONG, will produce error: myGroup:insert( list ) -- CORRECT: access display group with .view property myGroup:insert( list.view )
view.content
This is the display group that represent the content that holds all the display objects of every row. The rows table is accessed from view.content.rows
isLocked
boolean (true/false). If set to true, the tableView will be prevented from scrolling.
tableView:insertRow( [params] )
This method is used for inserting rows into the tableView (see example for usage demonstration). This function accepts the following parameters:
id (string, can be used to identify row)
width, height (numbers)
rowColor, lineColor (tables with r, g, b, a values)
isCategory (boolean, true/false - will cause row to "stick" to top of tableView)
listener build 2012.721 (function - see Row Events section)
onEvent (function - see Row Events section)
onRender (function - see Row Events section)
tableView:deleteRow( rowOrIndex )
Use this method to delete specific row by index number, or by passing the actual row table (if in an event listener, that would be event.target).
tableView:getScrollPosition()
NOTE: If you're using build 2012.721 or later, this method is no longer available. It has been replaced with getContentPosition(). This will return the current yPosition of the tableView content. This is mostly used to mark a position of the tableView, and then scroll to that stored position using the scrollToY() method. Since tableViews can't be rotated, it's recommended you use this function to mark the content position, destroy the tableView, then recreate it in the correct position (orientation?) and use scrollToY() to return it to it's previous position.
tableView:getContentPosition()
[Requires build 2012.721 or later] See getScrollPosition() (above).
tableView:scrollToY( yPosition, timeInMs )
Make tableView scroll to a specific y position (Note: this will most-likely be a negative number). This method is normally used after the getScrollPosition() method. timeInMs is the time in milliseconds it will take to scroll to the desired position. If you want the scrolling to be instantaneous (no effect), set timeInMs to 0. The default value for timeInMs is 1500.
tableView:scrollToIndex( index, timeInMs )
Make tableView scroll to a specific row that has the index position 'index'. The top of the row will be at the top position of the tableView (plus topPadding, if any). timeInMs is the time in milliseconds it will take to scroll to the desired position. If you want the scrolling to be instantaneous (no effect), set timeInMs to 0. The default value for timeInMs is 1500.
When you call the insertRow() method with your tableView, you pass a table of parameters. Two of the parameters are onEvent and onRender, which correspond to your touch listener (onEvent) for that specific row item, and the render listener (onRender) which is responsible for creating the row's visual elements (all controlled by you). Please see the example for basic usage demonstration.
When you create your event listeners, they will have a table passed as the first argument, which includes the following keys:
event.name
For onRender listeners, this will be "tableView_onRender", and for onEvent listeners, it will be "tableView_onEvent".
event.tableView
NOTE: If using build 2012.721 or later, use event.parent instead. This is a reference to the tableView widget object that the row belongs to (onRender, onEvent)
event.parent
[Requires build 2012.721 or later] Direct reference to the tableView that the row belongs to.
1 2 3 4 | event.background</strong> [<strong>Requires build 2012.721 or later</strong>] Direct reference to the background rectangle for the row. <code> |
1 2 3 4 | event.line</strong> [<strong>Requires build 2012.721 or later</strong>] Direct reference to the vector line object for the row (appears at the bottom of the row, used to help visually separate it from other rows). <code> |
event.target
event.view
This is a reference to the display group that holds all of the row's display objects. If you create a display object for a specific row in your onRender listener (such as a text object, or an image), you MUST insert those objects into the event.view group or they will not get rendered properly, and may even cause memory leaks (see example for usage).
event.phase
For onEvent listeners, the event.phase will either be "press" or "release". For onEvent listeners, you should always test for a phase, and return true on success. As build 2011.701, two are available: "leftSwipe" and "rightSwipe". As of build 2012.721, another phase has been added: "tap"
event.index
This is a number that represent's the row's position in the tableView. For instance, if the first row item triggered the onRender or onEvent callback listener, the event.index would be 1.
This widget can be removed in the same manner as any other display object, by using display.remove() or the removeSelf() method. Example:
display.remove( list ) list = nil -- or... list:removeSelf() list = nil -- IMPORTANT NOTE: -- Don't forget to set the reference variable to nil! (as shown above)
Corona widget object.
After build 2012.721: Corona widget display object.
Some features require build 2012.721 or later. As of this build, the tableView widget is a display object and no longer has to be removed separately from its parent group.
The sync method description sample uses newList rather than widget.newTableView - was this intentional?
Additionally, would it be possible to get a complete list of the variables sync requires for this widget to work? I may be crazy but it seems like table entries such as { title="Hello" } isn't enough. I'm guessing that the widget requires a series of precise table entries in order to function?
@richard9: Nice catch! I changed the page to use widget.newTableView instead of newList. I think in the example I was working on, I localized widget.newTableView into newList but forgot to include that when I pasted it here.
Also, in a later update, TableView list items will be more flexible--allowing you to put whatever you want into them.
Well, after pulling my hair out, I found out why the source code posted doesn't work. (Really.) It took a couple of hours of pulling my hair out, but here's what needs to be updated:
Changes Necessary: (1st Source)
- missing a comma after the first title={} table.
- missing a comma after the first subtitle={} table.
- (Optional) For the subtitle to be visible:
-- Comment out "size = 18,"
-- Comment out "top = 20"
- (Optional) On Windows the fonts aren't there so comment those out. Is "HelveticaNeue-Bold" some default part of xCode?
Changes Necessary: (2nd Source)
- ITEM 1: title and subtitle need to be converted into tables with label fields.
- ANOTHER ITEM: Same thing - convert title and subtitle.
- ANOTHER ITEM: Comment out baseDir = system.DocumentsDirectory
- In the "Another Item" table, comment out baseDir = system.Documents (Errors unless you do this.)
- (Optional) It doesn't seem to matter whether the remaining base command is base or baseDir - maybe that just affects how non-default resources are set?
Minimum Required Code
- You can actually have empty table entries. {} works, so long as you have a comma (unless it's the last item!)
- As mentioned, title and subtitle need to be tables:
-- Incorrect: title="blah",
-- Correct: title={label="blah"},
In fact, this actually loads:
1 2 3 | local itemData = { {},{} } local myList = widget.newTableView{} myList:sync(itemData) |
Requests
- Can't state this enough, but I'd really love it if this widget could nest categories seperately. Because the categories are being listed as table data, anybody who wants to use this function is probably going to have to write a brand new sorting function to somehow do the same work that the List View 2 Template does.
Something that could let us use table.sort() without having to write an entire class about sorting this widget would be awesome!
(The biggest reason I'm not using Listview2 myself is that it has no legible way to use adjustable row height.)
Cheers! :)
Ah, three more discoveries:
1. When specifying the icon={} table, your icon will NOT show up unless you specify height and width.
2. If you don't specify onRelease, the arrow is also hidden. ("hideArrow")
3. The function used by onRelease must be specified before the item table. If you don't, the widget will treat it as if you didn't specify onRelease.
Does the table view support custom table cells? If it does, is there any hidden api's to do it. ;D
+1 for cell customization. I'd like to put a segmented control in the cell!
How about at least the right arrow as a button (disclosure button in iOS)?
I've been successful with a test app using this widget but I've noticed the following:
- the arrow looks a little fuzzy when rendered on my iPhone4 (perhaps the @2 image isn't being used?)
- the scrolling is not smooth like lists done in the native API
- the category bar gets "stuck" on screen with the rest of the list moving below it at times
- I'm using Japanese in the list data and for some reason the last character in the text used in the category title is missing when displayed on the device but visible when viewed in the simulator
This widget would be useful to us if it accepted a list of display objects per set, with optional tap/touch event handlers, and dynamic content height. Example below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | label1 = display.newImage( 'myFirstSetLabel.png' ) local item1 = display.newGroup() local item2 = display.newGroup() item2:addEventListener( 'touch', someListener ) --optionally set listeners outside of TableView's control label2 = display.newImage( 'mySecondSetLabel.png' ) local item3 = display.newGroup() local item4 = display.newGroup() local itemData = { { label= label1, items= { { obj = item1, tap = someTapListener --optionally add listeners with TableView }, { obj = item2 }, } }, { label= label2, items= { { obj = item3 }, { obj = item4 }, } } } local myList = widget.newTableView() myList:sync( itemData ) |
This way you easily have customization of list-items & event handlers, and the ease of throwing them into a nice scroll-view. Thanks CoronaSDK!
seems that the alpha values for background , rowColor and downColor are ignored or am i missing something...would like to make transparent rows...
is there a way to specify a current selection in a widget.tableview and leave it hilited .... mytableview.row:select(21) for example
Is there a way to always show the bottom items of the list rather than the top items? There is a scrollToTop, but is there a scrollToBottom?
sembleton, domingoaj, seems like no to both.
I think sembleton's problem can be solved through code without too much anger-manangement using the non-widget listview. domingoaj's bottomlist method would be much harder to achieve since the template listview lacks any way to jump to a specific entry AFAIK.
The possibility to jump to a specific entry is for me also a absolutely must have.
The list is really fast with many entries (tried with 5k), but with over 1000 entries its unaccectable to scroll the hole list down, to reach the bottom. Especially because the speed of scrolling rises not proportional to the wipe speed, and therefore its much to slow for large lists.
@Johnathan
Is it planed to itegrate a function to jump to a specific list element or category?
Sorry for asking again, but for my project, this is a very important information.
Will there be soon a function to scroll the list to a specific entry or category, or rather, to jump to any y coordinate. And maybe a approximate release date for this feature.
thank you very much!
Wow! Lots of changes. Can't wait to try it! :)
@jonathanbeebe:
Previously, we could do onSwipe right & left events. It is not clear to me how this would work now. This is a feature we are using in our published apps.
Thanks.
@sunmils: Those methods are now in place :-)
@MeApps: Right now, since each row is just a display group, you could create a rectangle and assign a touch event listener, and handle swipes as you would with anything else :-)
@jonathanbeebe:
very nice! Cant`t wait to try them.
@meApps:
just if you still not sure how to implement a swipe effect. As far as i remember correctly, the director class (code sharing area) uses a swipe effect which should be easy to adapt to the tableView.
hope that helps.
How the method "deleteRow" should work?
Because I was trying to delete the item 4 (for exemple), and it's staying on the screen, but behind the tableView!
It's not actually removing the item...
Is it correct?
It's worth noting that this widget ONLY works if you specify some options when calling widget.newTableView(). If you specify nothing it will fail.
MauricioM: I have no idea why, but as best I can tell list:deleteRow() currently only deletes the last row, no matter what number you specify.
Additionally, if I delete a row and then attempt to use a scrollToIndex, the list blanks out.It seems that, at least in Windows sim, scrollToIndex() can only scroll DOWN and not UP.
Perhaps both of these will be fixed in a later build. (Currently using 652)
Tried building a version of this with a custom table of only 10 entries. I've hooked up the list:deleteRow() function to a widget button for ease of testing. (Build 652, W7x64, iPad view)
1. list:deleteRow(1)
In this case (as opposed to the example code), no entries are deleted. There are still 10 entries. No other viewable behavior.
2. list:deleteRow(10)
In this case, MauricioM's comment comes true. Whenever you drag the list with your mouse/finger the deleted entry pops in, on-top of your list. Let go, and it disappears behind it.
It seems that this behavior can be easily masked - if you call scrollToIndex() immediately after, it (sometimes) cannot be seen, probably because it is appearing above the top of the screen rather than anything else.
3. No matter what you delete, this begins to cause some unusual behavior. For example, if I delete [5] and click on the new [8], [8]'s name is deleted and replaced with [8]. (So there are two [8]'s!)
I am getting the same behaviors as @Mauricio and @richard9
EDIT: blah. Could be entirely wrong. Late night posting strikes again. It could be perfectly solid and I'm calling some non-static code...
@richard9: did u submit a bug report for the delete behavior?
Yeah, bug submitted.
the row phase event needs to have a swipe left and right as it did before.
comment below
The widget.newButton has the below event phases for the onEvent listener:
event.phase
For onEvent listeners only, this is so you can detect which "phase" of the event is occurring. Phases include: "press", "moved", and "release".
The widget.newTableView for each row instance only have "press" and "release". Please include the "moved" phase as well just like the newButton.
Anytime I add a display.newRect to use the moved phase of a touch event it scroll of the tableview gets locked up and its onEvent is over written by the touch event attached to the display.newRect
Nevermind, late night brain error code...
Hi!
How to make row title with multi line!
Thanks!
Is it possible to use a maskFile that is located at system.DocumentsDirectory?
Thanks
Has anyone had any luck with making a "selection" row? (in iOS Mail, this would be tapping on a mail to make it appear blue in the list entry.)
It's not hard to change the color of the currently tapped row; .rowColor does just fine. But the problem is, it's permanent. I can't turn it back white later.
1 | myList.content.rows[previousRow].rowColor = {255,0,255,255} |
...works fine in terms of addressing a different row and assigning a new value, but it won't re-render that way. Calling .reRender == true through onRowTouch, on the other hand, simply causes an error. So I'm not sure how to redraw the row.
Calling re-render for a row that is not event.index just seems to cause an error (particularly in onRowTouch) so I'm stumped on this one.
In onRowTouch I have been able to get a row to update using :
row.reRender = true
(notice = and not == in your example)
Outside of a touch event I could not find a method to get the list to redraw (list:reRender() ?) so I iterated through rows setting
list.content.rows[rowPos].reRender = true
and then called the following:
1 2 | local pos = list:getScrollPosition() list:scrollToY(pos,0) |
Hi there,
nice widget, the TableView. But I am not able to get the Android-version running. Don't know, if it's a bug or the inability of my fingers :-)
Filed here at the bugs-section: https://developer.anscamobile.com/forum/2011/11/24/newtableview-and-android
In short: On my iPhone, the TableView shows "Pressed" inside the row, when tapped. On the Nexus One, nothing happens. The TableView is moved slightly up or down.
Edit: Any ideas, or does anyone have the same problems? The example doesn't work as expected on Android either.
Cheers,
Jörg
If i change the widget sample-code a bit, with adding two more categories, i have a little error.
Just the next of the last category is pushed out correctly by the last one. The other ones prior go underneath through instead pushing away. A little difficult to explain.
Just add this lines to the widget sample-code @ line 148 and watch the top by scrolling through the list:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | -- make four categories if i == 1 then isCategory = true; rowHeight = 24; rowColor={ 174, 183, 190, 255 }; lineColor={0,0,0,255} end if i == 4 then isCategory = true; rowHeight = 24; rowColor={ 174, 183, 190, 255 }; lineColor={0,0,0,255} end if i == 20 then isCategory = true; rowHeight = 24; rowColor={ 174, 183, 190, 255 }; lineColor={0,0,0,255} end if i == 25 then isCategory = true; rowHeight = 24; rowColor={ 174, 183, 190, 255 }; lineColor={0,0,0,255} end |
edit: bug report sended
Are there other examples of this where subtitles, icons and the little right side >'s are used?
(Futurama voice) Great news everyone! (Some?) fixes are coming! Sounds like daily builds will soon see...
robmiracle: It should just be a matter of adding stuff to the onRender event - there's no default icons/subtitles. So, an easy way to add subtitles and icons would be like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | -- onRender listener for the tableView local function onRowRender( event ) local row = event.target local rowGroup = event.view local text = display.newRetinaText( "Row #" .. event.index, 12, 0, "Helvetica-Bold", 18 ) text:setReferencePoint( display.CenterLeftReferencePoint ) text.y = row.height * 0.5 local subtitle local icon if not row.isCategory then text.x = 15 text:setTextColor( 0 ) subtitle = display.newRetinaText("whatever", 12, 10, nil, 12 ) icon = display.newImage("file.png", 2, 2) end rowGroup:insert( text ) rowGroup:insert( subtitle ) rowGroup:insert( icon ) end |
I'm pretty sure I got the display.newImage arguments wrong (:P) but that's the gist of it - declare what you want to make inside the render event, and make sure to insert it into the rowGroup.
(The > you can handle the same way, as a placed image.)
Right, and in my onRender function, I'm printing that I'm getting the text I'm looking for, but I don't get anything displayed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | local myList = widget.newTableView{ top = 60, width = 320, height = 320, maskFile = "mask-320x320.png", bgColor = {0,0,0,255} } mainGroup:insert(myList.view) local itemIcon = { image = "tacky_bullet.png", width = 32, height = 32, base = system.ResourcesDirectory, paddingTop = 20, paddingLeft = 10, paddingRight = 12 } local function onRowRender(event) print("row render") local row = event.target local rowGroup = event.view local id = row.listIndex --print_r(row) print(id) print(stories[id].title) local icon = display.newImageRect(itemIcon.image, itemIcon.width , itemIcon.height ) icon:setReferencePoint(display.TopLeftReferencePoint) icon.x = itemIcon.paddingLeft icon.y = itemIcon.paddingTop local text = display.newRetinaText( stories[id].title, 12, 0, "Helvetica-Bold", 18 ) text:setReferencePoint( display.CenterLeftReferencePoint ) text.y = itemIcon.paddingTop text.x = itemIcon.paddingLeft + itemIcon.width + itemIcon.paddingRight text:setTextColor( 0 ) -- must insert everything into event.view: rowGroup:insert( text ) return true end |
I haven't tested since adding the icon data, but when this runs, I see my prints in the terminal window so I know onRender is firing. But I get a lovely white screen (well I have my two tabBar controllers showing at the top and bottom of the page).
Another clue that something is amiss is that I'm setting the background color to black, but its showing white.
I'll take a look at my code when I get home, but...in your example you're not inserting the icon into rowGroup.
Are you saying nothing from that example shows at all?
I think I got it. There was some things I was doing wrong using code I got from the docs page I suppose as opposed to the sample app.
By all means please share - I like to avoid pitfalls even if they are simple ones :)
Well first problem: Mixing the screen handling features of the old community provided tabBarController and the Widget library way of doing things. I had too many competing display groups going on.
My next issue is that top=60 isn't relative to the tabBarController turned title bar + ad space. Setting it to top=110 now puts it in the right place.
Next pitfall: I was creating objects like:
icon
title
etc.
But the code does:
row.title
While I'm not sure this actually did anything different, I switched to the row method and for the most part things started working.
"leftSwipe" and "rightSwipe" -- NICE!
Is there a way to support paging of rows?
In the native iOS table-view, you can pull down on the last or first row to get more results.
Has anyone implemented this?
If not, has anyone "simulated" it buy only call "sync" with a subset of results and swiping on the last row to fetch more content.
Thanks
has anyone discovered the events for these other actions?
Can you use populatefromXML like you could in Coronaui to make the list from an XML file?
"leftSwipe" and "rightSwipe" does not work :-( I am using .702. When I swipe the press phase gets excuted and stays "pressed"...
where are these events documented? I really want a swipeDown so I do a reload!
Under Row Events event.phase (right above this page)
This documentation was great, but I would like to point out an error in your sample code that may cause some confusion. The sample code lists the following:
base = system.ResourcesDirectoryThe API lists the attribute as "baseDir", not "base". FYI, the API is correct and the sample code is not.