The Corona Labs Blog
Posted on . Written by

If you missed either of the previous Widgets 2.0 tutorials, please read them to learn about the additional new widgets.


Today’s tutorial explains the remainder of the new Corona widgets. These include the following, available in the latest Daily Builds.

  • table view  allows you to create scrolling lists of data.
  • scroll view — allows you to create scrolling content areas.
  • picker wheel — supports custom columns and extraction of values from rows within the “selection area.”
  • progress view — an incrementing “fill bar” to indicate what percentage of a task is complete.

Important!

Please refer to the current Daily Build documentation as you experiment with new widgets. Several things have been changed or renamed from the old widgets, so you should study the documentation carefully. To assist you, we have included a migration guide to help you make the change. From the linked API page, scroll down to the widget.* section. You’ll find the updated references and migration guide there.

Getting Started

As with several other Corona libraries, you must “require” the widget library to use widgets:

local widget = require( "widget" )

Table View

The table view widget is a scrollable view of table rows with “momentum scrolling” and the ability to set particular rows as “categories” that will lock in place when they reach the top of the widget view. The 2.0 version of the table view also features new customization options including the friction and max velocity of the scroll behavior.

Event Listener

In contrast to other widgets, the table view can accept four event listeners, each used for a particular purpose.

1. Movement Listener

Used to listen for movement-based table view events, with the following events:

  • event.limitReached indicates that the table view has reached one of its limits.
  • event.direction returns the direction the table view is moving in.
local function tableViewListener( event )
   print( event.direction )
   print( event.isLimitReached )
end

2. onRowRender

Used to listen for table view row rendering events with event types of rowRender. This listener is only initiated on initial rendering of your table view rows. In your listener, event.row is a reference to the table view row that was rendered. The following example auto-fills each row with a text label that matches the row number, centered vertically in the row.

local function onRowRender( event )
   local phase = event.phase
   local row = event.row

   local rowTitle = display.newText( row, "Row " .. row.index, 0, 0, nil, 14 )
   rowTitle.x = row.x - ( row.contentWidth * 0.5 ) + ( rowTitle.contentWidth * 0.5 )
   rowTitle.y = row.contentHeight * 0.5
   rowTitle:setTextColor( 0, 0, 0 )
end

3. onRowUpdate

Used to listen for table view row rendering events, with event types of rowUpdate. This listener is only initiated when previously off screen rows become visible again. In your listener, event.row is a reference to the table view row that was made visible. The following example returns a response when a particular row enters the screen.

local function onRowUpdate( event )
   local phase = event.phase
   local row = event.row

   print( row.index, ": is now onscreen" )
end

4. onRowTouch

Used to listen for table view, with event phases of press, release, swipeLeft, and swipeRight. In your listener, event.row is a reference to the table view row that you interacted with. Note that the table view press phase is triggered after a very short touch-and-hold on the row. This is by design to prevent the row from considering a momentary or accidental “flick” tap event as a “press” on that row.

local function onRowTouch( event )
   local phase = event.phase

   if ( "press" == phase ) then
      print( "Touched row:", event.target.index )
   end
end

Standard Table View

Declaring a new table view is accomplished as follows:

local tableView = widget.newTableView
{
   left = 0,
   top = 52,
   width = 320, 
   height = 366,
   maskFile = "assets/mask-320x366.png",
   listener = tableViewListener,
   onRowRender = onRowRender,
   onRowUpdate = onRowUpdate,
   onRowTouch = onRowTouch,
   maxVelocity = 1.0,
   friction = 0.972,
   noLines = false,
}
  • left and top (optional) — the position where the table view will be created. The default values for left and top are 0, 0.
  • width and height (required) — width and height of the table view. At this time, these parameters only control the “behavioral bounds” of the table view, not the visual boundaries. In other words, the height parameter specifies the “internal height” of the table view in which rows are considered to enter and exit the view. If you wish to create a table view that visually spans just a portion of the screen, you must define the proper width and height parameters, create a matching bitmap mask, and apply it using the maskFile parameter.
  • maskFile (optional) — if you set a custom width or height, you should specify the maskFile parameter, which is the filename of the bitmap mask. This mask should mimic the defined width and height.
  • listener, onRowRender, onRowUpdate, onRowTouch (optional) — the table view’s corresponding event listener functions, according to each type discussed above.
  • maxVelocity (optional) — use this to limit the maximum scrolling speed of the table view widget. The default value is 2.
  • friction (optional) — this determines how fast the view moves when it’s “flicked” up or down. The default value is 0.972, which should be sufficient for most cases. In previous versions of this widget, you were not able to set this property. Now, you can set the “flick speed” faster or slower. Set to 0.0 to effectively turn off “momentum scrolling.”
  • noLines (optional) — set to true to remove the lines from around each row.

Next, you must create rows in the table view. This is done via the tableView:insertRow() function, with four parameters.

tableView:insertRow
{
   isCategory = false,
   rowHeight = 24,
   rowColor = { default={ 255, 255, 255 }, over={ 0, 175, 250 } },
   lineColor = { 150, 150, 150, 50 },
}
  • isCategory — sets a particular row as a category. This means that the row cannot be clicked on, and when it reaches the top of the view, it will “lock” in place as other rows scroll beneath it, and “unlock” when the view is scrolled back down.
  • rowHeight — integer to set the height of a particular row.
  • rowColor — consists of two sub-tables: default and over, each being a table of RGB + alpha values for the row background color. The default color is the row background color in the normal state, while over is the highlight color when the row is pressed.
  • lineColor — a table of RGB + alpha values for the row border color.

Scroll View

The scroll view widget is a scrollable area larger than the screen size — a large image, for example — with “momentum scrolling.” The 2.0 version of the scroll view features new customization options including the friction in which a screen “flick” will affect the scroll speed.

Event Listener

The event listener for a scroll view is simple. It reveals the usual phases of began, moved, and ended, and additionally it reveals the event.limitReached value which is triggered when the scroll view reaches one of the four directional limits.

local function scrollListener( event )

   local phase = event.phase
   print( phase )

   local direction = event.direction

   -- If the scrollView has reached it's scroll limit
   if ( event.limitReached ) then
      if ( "up" == direction ) then
         print( "Reached Top Limit" )
      elseif ( "down" == direction ) then
         print( "Reached Bottom Limit" )
      elseif ( "left" == direction ) then
         print( "Reached Left Limit" )
      elseif ( "right" == direction ) then
         print( "Reached Right Limit" )
      end
   end
   return true
end

Standard Scroll View

The first step is to create the “base” for the scroll view. This is done by setting the basic position and size parameters, plus the total allowed scrollWidth and scrollHeight that the view can scroll.

local scrollView = widget.newScrollView
{
   left = 10,
   top = 100,
   width = 300,
   height = 350,
   maskFile = "assets/scrollViewMask-350.png",
   scrollWidth = 465,
   scrollHeight = 670,
   friction = 0.972,
   listener = scrollListener,
}
  • left and top (optional) — the left and top position of the scroll view. You can also position it normally by setting its x and y position afterward.
  • width and height (required) — the width/height of the scroll view, in relation to the total scroll distances. Note that these values do not trim (mask) the scroll view to this size on the screen; they simply determine the width and height that the view will scroll until it stops and “springs back” on reaching a directional limit. For example, if your scrollWidth is 1000, and the width is 800, the view can be scrolled for 200 pixels. If you change the width value to 100, the view can be scrolled 900 pixels. Remember that if you’re working with a scroll view that is smaller than the screen size, you need to create a bitmap mask and specify it as the maskFile parameter, similar to the table view masking procedure.
  • maskFile (optional) — if you set a custom width or height, you should specify the maskFile parameter, which is the filename of the bitmap mask. This mask should mimic the defined width and height.
  • scrollWidth and scrollHeight (required) — these are the width and height of the total scrollable content area. For example, if you place visual content of 1000 pixels wide inside the scroll view, you should set the scrollWidth to 1000 as well. IMPORTANT: this value can not be changed after the scroll view has been created!
  • friction (optional) — this determines how fast the view moves when it’s “flicked” up or down. The default value is 0.972, which should be sufficient for most cases. In previous versions of this widget, you were not able to set this property. Now, you can set the “flick speed” faster or slower. Set to 0.0 to effectively turn off “momentum scrolling.”
  • listener (optional) — the name of the slider’s event listener function.

Adding Content to a Scroll View — or Other Widgets!

In scroll view 2.0, in addition to the usual placement of visual content inside the view, you can now insert other widgets and achieve the expected behavior. Because the scroll view is a display group itself, you simply add these items to the scroll view (or remove them) as you would when dealing with any other display object and group.

--create a image and add it to the scroll view
local background = display.newImageRect( "assets/scrollimage2.jpg", 768, 1024 )
background.x = bg.contentWidth * 0.5
background.y = bg.contentHeight * 0.5
scrollView:insert( background )
--create a "checkbox" widget and add it to the scroll view
local checkboxButton = widget.newSwitch
{
   left = 130,
   top = 200,
   style = "checkbox",
   id = "Checkbox button",
   onPress = onSwitchPress,
}
scrollView:insert( checkboxButton )

Picker Wheel

The picker wheel mimics the iOS picker wheel, for example the built-in time selector for the “alarm clock” app. You can build the wheel from a flexible set of columns, with custom data for each column and a default starting index for each column.

Configuring the Columns

In this tutorial, we’ll configure a simple date selector wheel. First, let’s populate three column tables: months, days, and years.

local months = { "January", "February", "March", "April", "May", "June",
                 "July", "August", "September", "October", "November", "December" }
local days = {}
for i = 1, 31 do
   days[i] = i
end
local years = {}
for i = 1, 44 do
 years[i] = 1969 + i
end

Declare the “columnData” Table

Next, we’ll fill the columnData table. This is simply a table with a sequence of sub-tables — those we configured above — and some parameters which will dictate the visual appearance of the columns within the picker wheel.

local columnData = 
{ 
   --months column
   { 
      align = "right",
      width = 150,
      startIndex = 5,
      labels = months
   },
   --days column
   {
      align = "left",
      width = 60,
      startIndex = 18,
      labels = days
   },
   --years column
   {
      align = "center",
      width = 80,
      startIndex = 10,
      labels = years
   }
}

Notice that you must declare the columns in the left-to-right order that they should be displayed within the picker wheel, and each column contains the following parameters:

  • align (optional) — alignment of the text elements within the column. Default is center alignment.
  • width (optional) — specific width of the column. By default, columns are split evenly between the picker wheel’s viewable area.
  • startIndex (optional) — the starting index of the column. Must be set to an index within the allowed range, i.e. 1 to 12 for the months column.
  • labels (required) — the table of column data to populate the column with.

Declare the Picker Wheel

The final task is to declare the actual picker wheel. The code is essentially as follows:

local pickerWheel = widget.newPickerWheel
{
   left = 0,
   top = 210,
   font = native.systemFontBold,
   fontSize = 10,
   columns = columnData
}
  • left and top (optional) — the left and top position of the picker wheel. You can also position it normally by setting its x and y position afterward.
  • font (optional) — the font that will be used when rendering column rows. Default is “native.systemFontBold”.
  • fontSize (optional) — the font size for the picker wheel columns. Default is 22.
  • columns (required) — the columnData table that we configured above, with the three associated columns of months, days, and years.

Picker Wheel “:getValues()” Function

Reading values from the picker wheel is simple using the :getValues() function. This can be executed programmatically at any point, for example on the press of a “save” button or similar.

local values = pickerWheel:getValues()

print( "Column 1: " .. values[1].value )
print( "Column 2: " .. values[2].value )
print( "Column 3: " .. values[3].value )

As you can see, each column is returned in sequential order in the values table, and you access the specific value from that column using the .value property.


Progress View

The progress view is a basic left-to-right incrementing progress bar, useful for asynchronous processes that you queue up within the app. One example is a collection of photos downloading one by one — hence asynchronously — in which you can use the progress view to indicate the percentage of the total process completed.

Standard Progress View

Since the progress view is not an interactive widget, you do not need a listener. Instead, just declare it as follows:

local newProgressView = widget.newProgressView
{
   left = 20,
   top = 240,
   width = 200,
   isAnimated = true,
   fillXOffset = 0,
   fillYOffset = 0
}
  • left and top (optional) — the left and top position of the progress view. You can also position it normally by setting its x and y position afterward.
  • width (optional) — the overall width of the progress view.
  • isAnimated (optional) — set to true if the progress change should be animated, false (or omitted) if the progress change should happen immediately.
  • fillXOffset and fillYOffset (optional) — Set these if you wish to position your progress view’s “fill” image at a horizontal or vertical offset. Default is 0. Note that fillXOffset applies to both sides of the progress bar; thus if you set the value to 20, the fill bar be offset to the right by 20 pixels and it will stop 20 pixels to the left of the total distance.

Progress View “:setProgress()” Function

Incrementing or setting the progress view fill bar is accomplished via the :setProgress() function, with a decimal value representing the percentage 0.0 (0%) to 1.0 (100%). The following example increments the progress bar in five even increments spaced across 10 seconds:

local inc = 0.0

local function progressIncrement()
   inc = inc + 0.2
   newProgressView:setProgress( inc )
end
timer.performWithDelay( 2000, progressIncrement, 5 )

Wrapping Up…

This tutorial concludes discussion of the current Widgets 2.0. We encourage you to download the latest Daily Build to get the most current versions of each widget. Remember, Daily Builds are a subscriber-only advantage, so please subscribe now to gain access to the latest Corona features and additions.

35 Responses to “New Widgets: Part 3”

  1. Mitaten

    How big is the performance difference between the new tableView and the old one? In terms of having _several_ rows inside it containing lots of display objects in each row? Is the new tableView generally better compared to the old one?

    Nevertheless great blog post

    Reply
  2. Hector

    Thanks for these tutorials, however, I noticed a couple of potential bugs you might want to check:

    1.- “TabBar Widget”: I tested the TabBar and WidgetDemo sample codes and I noticed that the background of the tabBar from the sample code looks weird, no matter what background image I have, I can’t make it look right. I also followed the tutorial No. 2 and got the same result as the sample code.

    2.- “Scroll View Widget”: I also tested the sample code, and apparently even though the parameter “horizontalScrollingDisabled” is set to true, I can still scroll horizontally. Not sure if it has to do with the length of the “lotsOfText” variable from the sample code.

    3.- “Picker Wheel Widget”: I tested the code from both this tutorial and the one in the documentation for build 1041, and I get the following Runtime error:

    ?:0: attempt to compare nil with number
    message
    stack traceback:
    [C]: ?
    ?: in function
    ?: in function

    I apologize in advance as it might be an error on my side for not following the tutorial correctly, but these are the errors I got while testing the new widgets

    Regards
    Hector

    Reply
    • Brent Sorrentino

      Hi Hector,
      Thanks for the notes. The issues #2 and #3 that you report are known regression bugs in Build #1041, but should be resolved in #1042, to be released very soon. Issue #1 might be in the same category, but I’ll check it out. I apologize for the inconvenience.

      Reply
    • danny

      Hey Hector.

      #2 is that there is no parameter named “horizontalScrollingDisabled” The parameter is named “horizontalScrollDisabled”

      Issues #1 & #3 are fixed and should be available in the next daily build.
      Thanks

      Reply
  3. Francisco

    “This tutorial concludes discussion of the current Widgets 2.0″

    I’m still looking forward to read the tutorial about customizing the appearance of the new widgets :-(

    Reply
    • Brent Sorrentino

      Hi Francisco,
      I chose the wrong words there… a theme guide will be created to help developers along, but we want to focus on squashing these pesky bugs first, and ironing out the usability/features issues first. Thanks for your understanding.

      Reply
  4. Chevol

    Brent The issues #2 and #3 are still present in build #1043! We have seen no progress on the widgets 2.0 or their bugs since Sat and the #1041 build. This has been wrecking havoc on my dev timeline. I’m now having to comment out all my newPickerWheels just so I can run code and develop again. There area too many little bugs still lingering around in these Widgets 2.0 to make them really usable. Where is the tableview row.rerender? What is the workaround/new way to do that? I appreciate all you guys hard work, I’m just worried about these new widgets becoming like the old ones and having absolutely no support with various issues/bugs.

    Reply
    • Brent Sorrentino

      Hi Chevol,
      I looked at the build notes for #1043 and it appears the fixes did not yet roll into that build, hence the same behavior. :( Let me check into this and figure out when those fixes can be expected to be made public. It should be any day now, sincerely.

      Also, the new widgets will not be allowed to wither on the vine. There will probably be some feature requests that take longer than others, especially if they apply to less common usage and “edge cases,” but in regards to a standard core base, we’re committed to making this happen. With a major revamp of an entire library like this, the growing pains and conversion issues are an unfortunate side effect. I appreciate your patience (and everybody’s patience), and also for noting these issues here and in the forums… we’re receiving all of them and taking the necessary steps as fast as we can.

      Reply
    • danny

      Hey Chevol.

      If you look at the amount of widget 2.0 bugs that have been fixed since they were released you should be re-assured. Having “No support” isn’t the case. All bugs reported are evaluated and fixed in a priority basis.

      Some bugs just take longer to fix than others.

      We are 100% committed to resolving any issues with widgets 2.0 as fast as possible and will not stop until we reach that point.

      Thanks

      Reply
  5. Sam Tannen

    Will it be possible to make a picker wheel out of rows instead of columns? This would be useful if you were populating the wheels with longer strings of text. Also, it would be great if you could use images instead of text.

    Reply
  6. Craig

    Hey Brent. Would you be able to check into the TabBar widget object:setSelected( buttonIndex, simulatePress ) method. The simulatePress parm appears to not be working. It won’t trigger the event even when true. Thanks

    Reply
  7. Jack01

    I am still using Widget 1.0 for the scrollView. It does not have the need for the new fixed scrollHeight. This allows a scrollView to grow, for example when used as a chat application.

    Reply
    • danny

      The new scrollView has support for dynamic scrollHeight sizing. It does this under the hood. That issue was fixed in daily build 1040

      Reply
  8. Olivier

    Hi,
    Is there a bug with “numChildren” property on ScrollView widget with widget 2.0 ?
    I inserted a lot of display objects on my ScrollView, and then I printed the srcollView.numChildren on terminal, it always says “2″
    Thanks
    Olivier

    Reply
  9. Jyrki

    Hey,

    I downloaded daily build 2013.1050 and tried to migrate my app to use the new ScrollViewWidget. My view items have, however, touch listeners bound to them, and with widget-v1 I used to call scrollView:takeFocus(event) if event.phase was “moved”. New ScrollViewWidget does not seem to have that method, though. So how this should be done with new widget library?

    Reply
  10. DeadpanDodo

    Same issue as Jyrki, the takeFocus is gone making the new scrollView useless.
    Going back to an old build, … as usual since 1028.

    Reply
  11. lessmsios

    Nice tutorial – and good to see that Corona is making headway on widgets.

    In using the Table View it would really be nice if the width and height controlled the visual view as well. I’m working on an app that is size-dynamic using percentages of areas of the screen for the layout. Table View works fine except parts of the bottom rows remain out of view. I know there is the maskFile option but to have such a file for ever possible device is not practical.

    Do you see the width/height controlling both the “behavioral bounds” of the table view AND the visual boundaries in the near future?

    Thanks in advance.

    Reply
    • Brent Sorrentino

      Hi Frank,
      The widget issues are being steadily fixed and addressed as they come into the queue, and fixes rolled out in daily builds. Do you have a specific issue which I should check on the status of?

      Reply
  12. Alberto

    Same issue as Jyrki and DeadpanDodo. How can now get that featured?
    Now my scrollview is not working :(
    I do everything in the Migration guide, I get no errors but the buttons in my scrollview doesn’t work.

    Reply
    • Ross

      Alberto, Mine is not either. I must not know how to migrate from Widget 1.0 to 2.0.

      I get no scroll action with 2.0. Going to try to go to an earlier non 2.0 version.

      Reply
  13. Peter

    Brent – Does anyone know when a tutorial for customizing widget appearances will be published? I’d love to learn how to do that with the new widget library…

    Reply
  14. Brent Sorrentino

    Hi Peter,
    The skinning guide is already underway, but we need to make sure it covers all bases and widgets, and is very clear on the use-cases that we imagine developers will commonly need. Thanks for your patience, we know this has been requested by several users.

    Brent

    Reply
  15. frank

    Brent,

    regarding the tableView, isn’t your sample code of onRowRender something ? How is the group populated. In widget 1.0, we used to set the event.view. How is this done in 2.0 and where is the change documented ?

    Thanks
    Frank

    Example from 2.0

    local group = event.view
    local row = event.target

    local text = display.newText( “My first TableView row!”, 0, 0, native.systemFont, 18 )
    text:setReferencePoint( display.CenterLeftReferencePoint )
    text.x = 25
    text.y = row.height * 0.5

    — you must insert any display objects into event.view group
    group:insert( text )
    end

    Reply
  16. Simon Fearby

    Is everyone else having to hold down the press a for a at leave 1000ms to get the “release” to fire in the onRowTouch( event ) widget.newTableView

    Reply
    • Brent Sorrentino

      Hi Simon,
      The docs haven’t been updated (yet), but the row touch (in about daily build 1080 and above) now features the following events:

      tap, press, release, swipeLeft, swipeRight, cancelled

      The “tap” being what you’d expect. As for the press/release, you’ll be required to “hold down” for a very short amount of time, but certainly not 1000 ms…. maybe 250 ms or so (I can’t remember the exact value but it’s around there).

      Brent

      Reply

Leave a Reply

  • (Will Not Be Published)