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




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?
danny
Not at the present time. The new widgets aren’t using vector objects (ie newRect etc).
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?
danny
You certainly can. A theming guide is going to be released this week.
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.
danny
The picker wheel currently supports standard and retina displays. Have you tried it on the iPhone 4 simultor skin?
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
Jorge Tapia
Yeah, that guide should be very helpful. Thanks and let us know when it’s available.
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.”
danny
Hey Hector.
The tab bar is currently missing a default theme. This will be rectified today.
Thanks
Hector
Excellent, thanks
Do you know how can I switch between iOS and Android themes?
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” )
Hector
Great stuff!, So cool that theme changes automagically depending on the device
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
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.
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.
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
danny
The fixed height issue is going to be addressed asap.
Jack01
Thx Danny,
I hope growth orientation (up or down) will also be an option then, shouldnt be to hard to implement.
danny
@Jack:
Can you specify what you mean by “Growth orientation” ?
I’m presuming you mean that the scrollHeight will extend with added content?
Jack01
Indeed the direction of the extension of a auto growing scrollHeight.
Jack01
Compare for example the scrollviews on the iPhone of the Messages app with Safari.
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!
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.
Darshit Vora
What about native type textfield that can be used in the scrollview also……? Can anyone tell me solution for the same…?
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
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
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?
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.
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?
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
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.
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
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.