This week’s tutorial covers how to visually stylize widgets. Although widgets will adopt the OS-like appearance by default, in many cases you’ll want to customize the appearance to suit the style of your app.

This part of the series (Part 1) covers the following widgets:

  • Button
  • Table View
  • Stepper
  • Spinner

IMPORTANT: some of the styling exhibited in this tutorial is available only in recent Daily Builds of Corona SDK. If you’re a Pro subscriber, please download the latest build now. If you’re a Starter user, all of these styling options will be included in the next public build of Corona.

NOTE: this tutorial is not about widget functionality — the following examples are focused on visual styling only.


Buttons can be created with the widget.newButton() API. You may construct buttons using one of three methods.

2 Image Files

This is the most simple button to construct. Just create two image files, one for the default state and another for the over state.


Next, specify the files as defaultFile and overFile respectively. Don’t forget to include the directory path if the files are located inside a subfolder.

2 Frame (ImageSheet)

This method uses two frames from an image sheet, one frame each for the default and over states.


For this method, include a reference to the image sheet in the sheet parameter. Then specify the frame numbers from the image sheet as defaultFrame and overFrame respectively:

9-Slice (ImageSheet)

This method uses 9 slices from an image sheet which are assembled internally to create flexible-sized buttons. As indicated in the following image, the 9 slices consist of the 4 corners (red), the 2 horizontal sides (green), the 2 vertical sides (yellow), and the middle fill.


Depending on the size of your button, the corners will remain at the size stated in the image sheet, but sides and middle will stretch to fill the entire width and height.

Remember that you’ll need 18 slices to construct the entire button: 9 slices each for the default and over states. While this requires more initial effort, the benefit is that sliced buttons can be set to virtually any size and still use the same image assets.

Additional Button Styling

In addition to the three core construction methods, all buttons share the following visual properties:

  • width and height — sets the width and height of the button. If you’re using 2 separate image files for the button, set these values to the width and height of the image. If you’re using the 9-slice method, you may set any width/height and the button will resize accordingly. If you’re using 2 frames from an image sheet, these values are optional since the size is inherited from the sheet options.
  • label — the text label that will appear on top of the button.
  • labelAlign — specifies the alignment of the button label. Valid options are left, right or center. Default is center.
  • labelColor — a table of two RGB+A color settings, one each for the default and over states. Please see the documentation for details.
  • labelXOffset and labelYOffset — optional x/y offsets for the button label. For example, labelYOffset = -8 will shift the label 8 pixels up from default.
  • font — the font used for the button label. Default is native.systemFont.
  • fontSize — the font size (in pixels) for the button label. Default is 14.
  • emboss — if set to true, the button label will appear embossed (inset effect). Default is true.
  • textOnly — if set to true, the button will be constructed via a text object only (no background element). Default is false.

Table View

The table view widget is created with the widget.newTableView() API. Table views that have more content than can be shown in the boundaries can display a scroll bar indicating how far down you’ve scrolled. Customizing this scroll bar requires 3 images in an image sheet. Each frame needs to be a square of equal size. These frames represent the top of the bar (red), the middle of the bar (green), and the bottom of the bar (yellow).


Here, the top and bottom frames are the “caps” of the scroll bar. The middle frame will resize to a variable height depending on the overall height of the table view and the number of items to scroll through — this mimics the scroll bar on many Mac OSX apps.

Once the image sheet is declared, simply pass a table named scrollBarOptions to the table view declaration with four parameters: sheet, topFrame, middleFrame, and bottomFrame:

Additional Table View Styling

In addition to a custom scroll bar, the table view allows for these visual properties:

  • width and height — sets the width and height of the table view. Remember that if you set these to a value smaller than the device screen, you should construct a mask to contain the bounds of the widget. See the documentation for details.
  • backgroundColor — a table of RGB+A color settings for the table view background. Default is white.
  • hideBackground — if set to true, the background of the table view will be hidden but still receive touches.
  • topPadding and bottomPadding — the number of pixels from the top and bottom of the table view in which scrolling will stop when it reaches the top or bottom of scrollable area. The default value for both is 0.
  • noLines — if set to true, lines will not separate individual rows. The default value is false.
  • hideScrollBar — if set to true, no scroll bar will appear in the table view. Default is false.

Row Customization

Table view rows are “rendered” and visual content is inserted using the tableView:insertRow{} function. To accomplish this, specify a listener function in the onRowRender parameter of the table view declaration. Next, write the rendering function similar to this:


The stepper widget is created with the widget.newStepper() API. This consists of a minus and plus button which can be tapped or held down to decrement/increment a value, for example, the music or sound volume setting in a game.

Visually, this widget uses 5 frames from an image sheet as follows:


  1. defaultFrame — this is the default frame with both the minus and plus sides active.
  2. noMinusFrame — this frame is used when the stepper reaches its minimum value, indicating no apparent result from a tap on the minus side.
  3. noPlusFrame — this frame is used when the stepper reaches its maximum value, indicating no apparent result from a tap on the plus side.
  4. minusActiveFrame — this frame indicates that a tap/hold occurred on the minus side.
  5. plusActiveFrame — this frame indicates that a tap/hold occurred on the plus side.


The spinner widget is created with the widget.newSpinner() API. You may construct a spinner using one of two methods, both of which utilize an image sheet:

  1. A single frame that will be rotated.
  2. A multi-frame animation that will be cycled.

Single Frame

This method utilizes one frame from an image sheet and rotates it to a delta angle defined by deltaAngle on the defined time increment of incrementEvery.


Multi-Frame Animation

Animated (multi-frame) spinners can be done using more complex image sheets. By definition, a spinner should spin, but you can use this method to create other kinds of “process underway” animations.


Once the image sheet is declared, create the spinner with three control parameters: startFrame, count, and time.

This concludes Part 1 of the “Stylizing Widgets” series. Keep an eye out for Part 2, coming soon!

Original article:  

Tutorial: Stylizing Widgets, Part 1