Of all the widgets included with Corona SDK, probably none provides as much potential utility as widget.newTableView(). Since it was introduced as part of the Widgets 2.0 library a year ago, it has probably generated more questions and additional feature requests in the forums than any other widget.

In this tutorial, we’re going to look more deeply at this powerful tool in the widget repository. Table views, or “list views,” are based on a mobile device paradigm where information is presented to the user in a series of single-column rows. In iOS, apps like Mail, Clock, and Contacts are obvious table views, but they are also used in countless other apps for menus and more.

In Corona, widget.newTableView() is a widget rendered in OpenGL and it has been built to emulate some of the more common features of a native table view. Under the hood, a Corona table view is built on top of a widget.newScrollView() which handles the up/down scrolling, the spring-like behavior, and the momentum-based scrolling. Starting with Graphics 2.0, the table view widget is also “self-masking,” meaning that Corona developers no longer need to create bitmap masks for table views that don’t occupy the entire screen.

Setting Up

In this tutorial example, we’ll create a table view that spans the full width of the screen, positioned just below an upper title bar and spanning down to a widget.newTabView() controller at the bottom. Here’s the basic code:

This creates an “empty” table view of the described size. Now, we must populate it with rows. This is done using the tableView:insertRow() method which inserts a blank, empty row into the view. To populate the widget in this example, we’ll use a table of items that contain a name and phone number:

Even with this setup, nothing is visually rendered to the table view at this point. Behind the scenes, the table view widget keeps track of which rows are on screen and draws only those rows when they are needed. This is accomplished by a function that we provide and define as the onRowRender parameter in the table view constructor. When this function is called by the table view, we’re provided with the handle to the appropriate row.

In Corona, each row is actually a separate display.newGroup(). Thus, in the row rendering function, we can create the necessary custom display objects and use the standard group:insert(object) method to insert each into the table view row.

How does the row rendering function know what to render within each row? Let’s inspect a typical example of the row rendering function:

The “magic” here is how we use the row’s ID number to map against the myData dataset. However, while this works great for simple cases, it won’t work for more advanced table views that contain “category” rows or empty rows for spacing purposes.

Introducing Passable Parameters

This feature has been available for some time but it hasn’t been discussed too much. Essentially, it provides a programmatic way to associate a row and specific data without relying on an ID number. Let’s use the same example but pass in parameters instead:

Now we can insert rows that contain data using the params table. Rows that have no data can be inserted without parameters (category rows, for example). Then, in the row rendering function, we can simply test to see if it’s a category row or not and render it accordingly:

Now the row rendering function doesn’t need to know anything about our data structure. This helps support the concept of Model-View-Controller (MVC). With this method, we can focus on our View and know which data to get without knowledge of how the Model is tracking the data. In most cases, this is a very good way to populate table view rows and it should make it easier to visualize data with the associated row.

Reloading the Table View With Status Bar Tap

This is a frequently-requested feature that’s easy to implement with a small amount of code. How the data is pulled in will depend largely on the app design, and it’s your responsibility to create a reload function that will be called as part of the tap handler. In most cases, the common functionality will be:

  1. Retrieve the updated data (new tweets or RSS feeds, for example).
  2. Flush (clear) the existing table view’s data.
  3. Re-insert the new data into the table view.

To flush/empty the rows as mentioned in step #2, we can call this function:

Now, to build in the “status bar tap” functionality, we can create a transparent rectangle where the status bar resides and add a “tap” event handler on it. This tap function will, not surprisingly, call the custom reload function. It’s that simple!

There’s one important aspect to note: since the rectangle object is invisible (.isVisible = false), we must set .isHitTestable to true so it reacts to tap/touch events.

Implementing “Spring Reloading”

Table view “spring reloading” is the technique of pulling down on the list to have it reload/refresh. This isn’t quite as easy to implement as the status bar tap method, but let’s walk through it.

There are some prerequisites to consider as part of the User Interface and User Experience (UI/UX) for spring reloading. When a user pulls down on a table view, it reveals the “background” of the app. In some cases, you may want to place something behind the table view, like a solid gray block. Also, most spring reload systems have an animated graphic that shows when the user has pulled down far enough, prompting them to release their touch. In iOS7, this is usually a spinner, which you may consider imitating via the Corona spinner widget. Ultimately, though, it’s your decision about how to handle the UI/UX for your app.

Before implementing spring reloading, let’s further examine the table view event system. Here’s our example table view constructor again:

As we’ve seen so far, the onRowRender function handles rendering and display of the actual table rows. Below that, the onRowTouch function handles “tap” and “touch” events on the individual rows (see the documentation for widget.newTableView() for usage examples). The final function, assigned to the listener property, is the function that will listen for scroll-related events on the table — and those events are of particular interest in spring reloading. Using this listener, we’ll be able to detect when the table view starts to move, when it’s still moving under momentum, and when it stops moving. We’ll also get an event.phase = nil when the table view reaches the limits of its scrolling, and it’s this event phase which indicates that we should reload the table view. Let’s look at the example code:

Notice how these conditional checks are used for different processes within the spring reloading:

  • In the “began” phase, we store the current content position of the table view. This way, when the user pulls down on the list, accidental reloads are not triggered if the user pulls down just slightly. We also set the flag variable needToReload to false in this phase.  If you wish to have an animation start to indicate they are beginning a spring reload, this would be a good time to start it.
  • In the “moved” phase, if the table view position changes by a set amount more than it started at, in this example 60 pixels, we set the needToReload flag to true. Since the user could still be dragging the table view at this time, we do not trigger the reload yet.  If you have a graphic or animation showing the user it’s now time to let go or they have pulled down enough, you would change the graphic/animation here as well.
  • In the “ended” phase, you can stop any animations you started in the “began” or “moved” phases.
  • In the final conditional check, we don’t want to reload only when the movement ends. If we did, the table view would reload every time the scrolling stopped on the table view. Instead, we’ll check for a series of conditions, starting with the event.limitReached event equal to true. This event has no phase (nil), so we also check for the absence of event.phase. Next, we check that the movement direction is “down” (typically, reloads don’t occur after scrolling up) and we also confirm that the needToReload flag is true (i.e. the user has pulled the view down enough distance). If all of these conditions are met, we call the reloadTable() function to start the table view reload.

In Conclusion

Hopefully this tutorial has shown you how to add some creative and useful features to the table view widget without touching one line of the open-source widget library. With the ability to use parameters to pass data to the row rendering function, plus these easy-to-implement reloading features, you can now supercharge your table views.

Visit site:

Tutorial: Advanced TableView Tactics