Using className with TableViewRows

Fingerprint on white paper

The TableViewRow has a property called className, which allows the system to optimize allocation of TableViewRow objects. Let’s look at how you would use this in an application.

For our example, we’re going to assume a simple contact management database, with names and pictures of the contacts. Some of the contacts however don’t have pictures. Our contact viewer will use a TableViewRow to display each contact, with or without a picture, depending on what’s in the database. So far so good, this all sounds very straightforward.

For each contact, we’ll set the title of the TableViewRow to be the name of the contact. In addition, for the contacts that have pictures, we’re going to add an ImageView to the TableViewRow. So we’ll end up with two kinds of TableViewRows, those with images, and those without. Here’s the code for this section of the program. In this example, we are getting the contacts from our SQLite database, so we’re iterating over the result set of contacts that will fit on the page currently displayed.

// Select one page of contacts. Let’s do the E’s (5th letter).

var contacts = db.execute(‘SELECT * FROM contacts WHERE alphaSort=?’, 5);

while (contacts.isValidRow()) {

var contactName = contacts.fieldByName(‘name’);

var picExists = contacts.fieldByName(‘hasPic’);

if (picExists == 1) {

// Get the image from the specified file

var picFilename = contacts.fieldByName(‘file’);

var picFile = Titanium.Filesystem.getFile(picFileName);

var pic = picFile.read();

row = Titanium.UI.createTableViewRow({

title:contactName,

className:’Pic’

});

var picView = Titanium.UI.createImageView({

image:pic,

width:40,

height:40,

left:250

});

// Add the pic to the TableViewRow

row.add(picView);

}

else {

// No pic for this contact

row = Titanium.UI.createTableViewRow({

title:contactName;

className:’noPic’

});

}

// Add the row (with or without a pic) to the TableView

myTableView.appendRow(row);

contacts.next();

}

Notice the className property when we create the TableViewRow—it’s either Pic or noPic, depending on whether we’re going to add an ImageView to the TableViewRow. The system keeps track of which type of row object to allocate when it comes time to reuse them, as when you transition your display from the E’s to the F’s, for example. If you try to attach an ImageView to a row that previously didn’t have one, it can cause performance problems in your application. In this case, you may see messages like these in Titanium Developer’s console:

[WARN] looks like we have a different table cell layout than expected. Make

sure you set the 'className' property of the table row when you have

different cell layouts

[WARN] if you don't fix this, your tableview will suffer performance issues

You can see sample code similar to this in a sample iPhone application in the upcoming TableView API Reference Guide. I’ll add another blog post when this guide is available, probably early next week.

1 COMMENT

  1. I really like your intent on this post. It’s exactly the kind of material for which I frequently hunger. However, I feel like I’m inferring the point, and I want to make sure I got it. So, here’s what I take away:
    – I think this is about optimizing resource usage
    – because the ‘noPic’ tableViewRow class has no image view added to it, it’s leaner on system resources
    – extrapolating from this, for a tableView that displays many rows for heterogeneous types (or even different views for homogeneous types), some tableViewRows may composed of several UI components, others may be much simpler
    – in such cases, setting the className property for each tableViewRow allows the system to allocate resources appropriately

    I think that’s the main point. Did I get it right?

    The part I don’t quite understand is the discussion about *re-allocating* tableViewRows. I understand that there are Cocoa best practices for managing resources when using tableViews. I recall something about allocating only enough rows to fill the current view, and adding and removing views as they scroll into or out of the tableView. So is using className a way to help Titanium follow a Cocoa best practice for doing something like this?

    Again, thanks for the post. I hope that I understood most of it, and that it’s just a minor clarification to get me to 100%.

  2. Ted,

    It makes sense if we assume a pool (or cache) of each TableViewRow type is being maintained for re-use purposes by Appcelerator. Then if 10 TableViewRow objects are visible on the screen, and you scroll down, we are just re-using objects that scrolled off the screen to display the new content.

    This saves the expensive cost of constantly allocating and freeing memory, speeding up the overall system, and minimizing usage of system resources. That’s exactly what you are talking about in your comment.

    Caveat: I don’t work for Appcelerator, though I wish I did. 🙂 These are just assumptions based on the way I’d have written the code, which are based on writing my own memory allocator in C back in the day. Ah, the memories of all night debugging sessions and Jolt Cola!

    John S.

  3. This is a great Tip.
    Once I started implementing the className on my Tablerows I could see a increase in prformance for sure.

    Though in 1.4.x there seems to be a performance hit when it comes to rendering the tableview for the first time.

    I have seen people talk about it in the QA and even in the ticket system. But it would be great to know if this is something that will be tackled in 1.5

  4. @John: Thanks for the reply.

    @Juan: Did the increases in performance come only with TableViewRows of different types in a TableView, or also for TableViews having all the same types of TableViewRows? (In other words, should I implement className even when it would seem like I don’t need to?)

  5. I used it on tableRows that follow the same “template”, so in one of my apps I have the same tableViewRow that shows up over and over. On a Iphone 3G you can see some big jumps in performance with large TableViews.

    Also, if I have 3 different possible tableRow templates( one with an image and title, one with two titles and another with just a picture I will have three classNames.

    I would consider it a good practice to always use the className when creating Rows unless all your rows are completeley different.

  6. How does className work in the case of a single table that frequently calls setData()?

    My entire application is built around a single table with rows of text. The user clicks a “Next” button, and new text is displayed with tableview.setData(). If I don’t use a unique className each time, the tableview explodes and vomits text all over the view.

    If the number of lines of text within the row is going to change after setData(), does that justify a new className?

    On a similar note, if you open the “table_view_refresh.js” example in Kitchen Sink and add a className to the row, it will stop drawing the icon when you do a refresh. What’s that all about?