The Corona Labs Blog
Posted on . Written by

If you missed New Widgets: Part 1 — now updated to reflect Widgets 2.0 — please read it to learn about the switch and segmented control widgets.


After much anticipation, we’ve finally released the majority of new Corona widgets! All new widgets share a common trait: each has been written atop a new foundation that is more flexible and stable. The new widgets include the following, available in Daily Build 1034 and beyond.

  • button — now featuring new styling options and expandable “9-slice” buttons.
  • tab bar — clickable tab bar with optional images and text labels.
  • slider — horizontal and vertical slider bar, useful for a range setting like app audio volume.
  • stepper — in which you tap (+) or (-) to adjust a value within a specified range.
  • spinner — a customizable “spinner”, otherwise known as an “activity indicator.”
  • 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.

In today’s tutorial, we’re going to learn about the first five on the list. Please be patient, as the others will be detailed in later tutorials.

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" )

widget-button

Button

The button widget has been rebuilt with some major improvements and visual styling options. All buttons still share the expected behavior: touch the button to receive a “began” event phase and optionally modify the button’s appearance. Release the button to receive a “ended” event phase and revert to the button’s original state.

Event Listener

As usual, you’ll need to create a basic listener function to report the user’s action upon the button.

local function onButtonEvent( event )
   local phase = event.phase
   local target = event.target
      if ( "began" == phase ) then
      print( target.id .. " pressed" )
      target:setLabel( "Pressed" )  --set a new label
   elseif ( "ended" == phase ) then
      print( target.id .. " released" )
      target:setLabel( target.baseLabel )  --reset the label
   end
   return true
end

Standard Button

The standard button setup is very straightforward:

local myButton = widget.newButton
{
   left = 10,
   top = 80,
   label = "Default",
   labelAlign = "center",
   font = "Arial",
   fontSize = 18,
   labelColor = { default = {0,0,0}, over = {255,255,255} },
   onEvent = onButtonEvent
}
myButton.baseLabel = "Default"
  • left and top (optional) — the left and top position of the button. You can also position the button normally by setting its x and y position afterward.
  • label (optional) — this is the default text label for the button.
  • labelAlign (optional) — the label’s horizontal alignment: “left”, “center”, or “right.” Default is center.
  • font (optional) — this is the font to use for your button label. Default is native.systemFont.
  • fontSize (optional) — set the font size on a per-button basis.
  • labelColor (optional) — this table contains two sub-tables with RGB label color settings for both default (off) and over (pressed).
  • onEvent (optional) — the name of the button’s event listener function.

Button “:setLabel()” Function

Use the :setLabel( “myLabel” ) function to change the text label on a button. Normally this is done in the button’s “began” phase, but you can change it at any time using this command. In the example above, we defined an optional string property for the button object: .baseLabel. This is set to the same value as label, and we revert the button label to this value on release.

Image-Based and “9-Slice” Buttons

Since buttons are rarely as basic as the above example, the new button widget allows you to design buttons using separate images, frames from an image sheet, and even resizable “9-slice” buttons where the button is composed of nine pieces: four corners + four sides + center fill. The setup of these “advanced” buttons is explained in the updated documentation. See the notes above detailing how to download this documentation.


widget-tabbar

Tab Bar

The tab bar widget allows you to create a stylized bar of selectable tabs, each with a standard “on/off” image or a unique image for each tab, along with a text label.

Event Listener

The event listener for the tab bar is created as follows. Notice that there is no need for a “phase” check, since the tab is either on or off.

local function onPress( event )
   local pressedTab = event.target
   print( pressedTab._id.." pressed!" )
end

Configuring the “tabButtons” Table

Each tab and its basic properties must be declared in a table. We’ll call this table tabButtons but it can be named whatever you want.

local tabButtons = 
{
   {
      id = "Tab1",
      label = "Tab1",
      labelColor = { default = {0,0,0}, over = {255,255,255} },
      onPress = onPress,
      selected = false
   },
   {
      id = "Tab2",
      label = "Tab2",
      labelColor = { default = {0,0,0}, over = {255,255,255} },
      onPress = onPress,
      selected = true
   },
   --more tabs can follow
}
  • id (optional) — id to assign to your tab bar button. Default is “button[num]” with [num] being the tab’s index in the series.
  • label (optional) — the text label to display below the tab image.
  • labelColor (optional) — this table contains two sub-tables with RGB label color settings for both default (off) and over (selected).
  • onPress (optional) — the listener function for when a tab is pressed.
  • selected (optional) — set to true to start the tab bar button in its “selected” state. You should only set one of these to true.

Tab Bar Placement

To place the tab bar on the screen, follow this basic code:

local tabBar = widget.newTabBar
{
   left = 0,
   top = display.contentHeight - 60,
   width = 240,
   height = 60,
   buttons = tabButtons
}
  • left and top (optional) — the left and top position of the tab bar. You can also position it normally by setting its x and y position afterward.
  • width (optional) — the overall width of the tab bar (not the width of each tab). Default is the display content width.
  • height (optional) — the overall height of the tab bar. Default is 50.
  • buttons (required) — reference to the tabButtons configuration table.

Tab Bar “:setSelected()” Function

Use the :setSelected( [integer] ) function to explicitly set which tab is selected. The value must be an integer between 1 and the total number of tabs.


widget-slider

Slider

The slider widget allows you to create horizontal and vertical sliders which the user can drag to a value between 0 and 100 percent. This is useful for controls such as sound/music volume or “channel levels” in an audio mixer utility.

Event Listener

The event listener for the slider is simple:

local function sliderListener( event )
   local slider = event.target
   local value = event.value
   print( "Slider at " .. value .. "%" )
end

As you can see, the listener returns event.value which is the percentage of the slider along its range. The listener also returns the event.phase. In a real-world example, the moved phase could be used to play a subtle “ratchet click” sound as the user drags the slider, and the ended phase could be used to perform an action when the user releases the slider.

Basic Slider

Here are both horizontal and vertical examples of the slider widget:

local slider = widget.newSlider
{
   orientation = "horizontal",
   width = 200,
   left = 50,
   top = 200,
   listener = sliderListener
}
local slider = widget.newSlider
{
   orientation = "vertical",
   height = 200,
   left = 50,
   top = 200,
   listener = sliderListener
}
  • orientation (optional) — orientation of the slider, either “vertical” or “horizontal”. Default is horizontal.
  • width or height (required) — width of the slider for a horizontal orientation; height of the slider for a vertical orientation.
  • left and top (optional) — the left and top position of the slider. You can also position it normally by setting its x and y position afterward.
  • listener (optional) — the name of the slider’s event listener function.

Slider “:setValue()” Function

Use the :setValue( [integer] ) function to set the position of the slider anywhere between 0 and 100.


widget-stepper

Stepper

The stepper widget is used for incrementing or decrementing a value. The control has two buttons aligned horizontally, one labeled with a plus (+) the other a minus (−). An additional feature is a continuous option, wherein if the user presses and “holds down” either button, the stepper value is incremented repeatedly. The longer the button is held down, the faster the increment will occur.

Event Listener

As usual, we’ll create a basic listener function to report the user’s action upon the stepper. We’ll also set up a currentNumber variable to report the current value of the stepper; this value is set to 10 to match the initialValue of the stepper (see below).

local currentNumber = 10
local function onPress( event )
   local phase = event.phase
   if ( "increment" == phase ) then
      currentNumber = currentNumber + 1 ; print( currentNumber )
   elseif ( "decrement" == phase ) then
      currentNumber = currentNumber - 1 ; print( currentNumber )
   elseif ( "minLimit" == phase ) then
      print( "MINIMUM VALUE REACHED" )
   elseif ( "maxLimit" == phase ) then
      print( "MAXIMUM VALUE REACHED" )
   end
end

Notice that the event.phase parameter of the listener will be one of the following four values. These can be used, for example, to play a sound or perform an action depending on the “state” of the stepper.

  • increment — the stepper increment button has been pressed.
  • decrement — the stepper decrement button has been pressed.
  • minLimit — the stepper minimum limit has been reached.
  • maxLimit — the stepper maximum limit has been reached.

For even more control, the stepper also sends the following three values to the event listener:

  • event.value — the internal count value of the stepper.
  • event.minimumValue — the internal minimum value that you defined for the stepper.
  • event.maximumValue — the internal maximum value that you defined for the stepper.

Basic Stepper

The stepper widget is easy to create:

local newStepper = widget.newStepper
{
   left = 150,
   top = 200,
   initialValue = 10,
   minimumValue = 0,
   maximumValue = 50,
   onPress = onPress
}
  • left and top (optional) — the left and top position of the control. You can also position the stepper normally by setting its x and y position afterward.
  • initialValue (optional) — set this to the stepper’s initial value. This represents the internal value of the stepper upon creation.
  • minimumValue (optional) — the minimum allowed value for the stepper. Default is 0.
  • maximumValue (optional) — the maximum allowed value for the stepper. Default is no limit.
  • onPress (optional) — listener function called when the stepper (+) or (−) is touched or held down.

widget-spinner

Spinner

The spinner widget provides a simple visual indication that something is occurring within your app that requires the user to “wait” before proceeding.

How does the spinner widget differ from the native.setActivityIndicator()? In addition to being a normal display object that you can position as needed, the spinner widget is 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 case you can use the spinner widget to indicate that the overall process is underway.

Basic Spinner

In contrast to most other widgets, the spinner requires no user response and thus doesn’t require an event listener. However, you can still customize the speed and rotation of the spinner when you create it. Here’s the most basic example:

local mySpinner = widget.newSpinner
{
   left = 250,
   top = 250,
   time = 1000
}
  • left and top (optional) — the left and top position of the spinner. You can also position it normally by setting its x and y position afterward.
  • time (optional) — the total time in which the spinner will complete a full animation. Default is 1000ms.

Controlling the Spinner

Controlling the spinner is easy: just use :start() and :stop() to control the behavior.

mySpinner:start()
mySpinner:stop()

And Even More Widgets…

We’ll introduce the remaining widgets in another tutorial. In the meantime, please download Daily Build 1034 or later to get started. 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 2”

  1. IcySpark

    For the new button widget is it not possible to now set the defaultColor and overColor of the button itself without resorting to using images?

    You have it for the labels, why not the buttons too?

    Reply
  2. Jorge Tapia

    Is there any way to customize the appearance and actually “theme” this widgets as you can do with native controls? I need to develop a game that relys heavily on widgets but they need to be fully customizable and I also need the Physics capabilities. Is Corona recommended for this?

    Reply
      • Nate

        Will widgets be scalable and retina aware, particularly the picker wheel, with custom themes? It appears the picker only works at 320×480 right now and that’s not very useful.

        Reply
        • danny

          The picker wheel currently supports standard and retina displays. Have you tried it on the iPhone 4 simultor skin?

          Reply
          • Nate

            I set my dimensions to 640×960 in config.lua and then it always comes out 1/4 the size. Is there a way to fix this?

          • danny

            @Nate.

            I see what you mean. Generally I have been testing on the lowest resolution setting in my config.lua file (480×320).

            I will see what I can do.
            Thanks

  3. Hector

    I followed this tutorial, and I successfully tried all the widgets with the exception of the one for the tabBar. I get an error related to a newImageRect:

    “ERROR: bad argument #2 to display.newImageRect(): filename or image sheet expected, but got nil.”

    Reply
    • danny

      Hey Hector.

      The tab bar is currently missing a default theme. This will be rectified today.
      Thanks

      Reply
        • danny

          The widget library does this automatically based on device, however you can also override it.

          IOS theme:

          widget.setTheme( “widget_theme_ios” )

          Android theme:

          widget.setTheme( “widget_theme_android” )

          Reply
      • Hector

        Hi Danny,

        Today I did another try to the tabBar widget sample, and now I get a different error (Build 2013.1040)

        “ERROR: widget.newTabBar: tab button default file expected, got nil”

        Do you know of the default theme was included in this build?

        Thanks in advance
        Hector

        Reply
        • Simon

          I too have experienced both of these errors while setting up a simple tabBar (2.0) and while updating apps using the older version. Would love some more guidance. Does the tab Bar now require a default image?

          Hector, have you found the solution to your error yet? thanks in advance.

          Reply
          • Don

            Simon/Hector/Danny,

            I am running into the same issue:
            ERROR: widget.newTabBar: tab button default file expected, got nil

            Did you gets find a solution/reason why we are seeing this?

            Greatly appreciate your input.

  4. Jack01

    The new scroll view seems pretty worthless for me.
    It has suddenly a required fixed scrollHeight. ScrollView is meant for simple static content only?

    For example when using the scroll view for a chat. So after a couple of messages, users can no longer scroll back.

    Further the scrollHeight is oriented from top growing to the bottom only, while in for example a chat like iMessage the scroll should be oriented from bottom growing to top.

    I hope this gets fixed soon. Thx

    Reply
  5. WarrenW

    Do you still have the checkbox and radio buttons available? Or were those removed? I really need to use those also. I have a problem using them in a scrollView and was told this owuld be fixed with widgets 2.0.

    Thanks!

    Reply
    • Brent Sorrentino

      Hi Warren,
      The switch widgets (checkbox, radio button, switch) are definitely still available. Please refer the the “New Widgets: Part 1″ tutorial, linked from this tutorial.

      Reply
  6. Ilya

    1. earlier widget.newButton () is a property of ‘style’ now it’s gone?
    2. 2012.971 in the assembly was a mistake – if you add widget.newPickerWheel () in widget.newScrollView () to scroll to its maximum value. This is fixed in the new version of widget?

    thanks

    Reply
  7. Craig

    with the tabBar:setSelected, the second parm (simulatepressevent boolean) appears to not be working. Is this a bug or is this parm no longer available ? Thanks

    Reply
  8. Claes

    The documentation says that label is optional on the tab bar buttons but if you don’t specify a label you get a runtime error on newText. Does not make any sense if it’s optional.
    I want to use an image based tab bar only without spacing at the bottom for labels. Images should be centered vertically.

    I also think that if you can specify an id on each button, why can’t you read that id in the function specified in onPress?

    Reply
  9. Dave

    Why is it that when I add buttons to the tab bar, the buttons are not added flush to the left edge of the screen? The tab bar centers the buttons, then stacks the next button about 2/3 the way on top of the previous button.

    Reply
  10. Dale

    The newSlider widget seems to not work with what I wanted my sliders to look like. With the old sliders, I was able to make them 50 px tall and 150 px wide, so I could update the value indicator text inside the slider area and not have to waste space having it on the outside. I also had special looking handles to follow the theme of my app.

    Now it looks like the slider frame is limited to a certain height, like 16px? And the handle is pegged to a certain width? Probably I’m just not doing my image sheet correctly. Is it possible to have sliders with a height different from some standard height, and handles of different widths from some standard width?

    Reply
    • Treb Stewart

      Hey Dale,

      I’ve spent the last few hours updating to the new slider, and my limited experience would say to check and make sure you’ve specified handleFrameHeight and handleHeightWidth in your widget,newScrollView call, I’m using a 24×34 handle and it’s working fine for me.

      Also if the frames were setup incorrectly then it could also likely cause that problem. If you use SpriteSheetPacker then it outputs a handy text file that you can check the values against.

      -Treb

      Reply
  11. Steve Taylor

    First off, great job in getting the new widgets out. Over a long long weekend I got everything ported (for me: buttons, sliders, check/radio, and tableview). My challenge is I need to the “row.reRender” when tableView widget. My use case is upon touch I change the image I shown (essentially alternate between On/Off images). Any thoughts would be greatly appreciated.

    Reply
  12. Steve Taylor

    I apologize for the hideous grammar. Try 2:
    My challenge is that I need to utilize “row.reRender” which was available in the 1.0 tableView Widget library. My use case is upon touch, I change the image shown (essentially alternate between On/Off images for a given row). I thought about deleting all rows and re-adding them back in but this doesn’t seem so bright. Any thoughts on how to do this would be greatly appreciated.

    Here is the Widget 1.0 documentation: http://docs.coronalabs.com/api/library/widget/newTableView.html

    Reply
  13. Irina König

    Is the segmentedControl really working? I have the latest daily build (1076) and I have used exactly the example you gave, but it looks strange. I am just able to select s1. If I tap s2 or s3, nothing happens, like they would be inactive.

    Reply

Leave a Reply

  • (Will Not Be Published)