UITableView
is arguably one of the most important UI classes in UIKit, for its use in so many interfaces, in fitting with the dominant list-based UI paradigm in iOS.
The way UITableView
is constructed makes it really well suited to displaying dynamic data, through the use of the delegation pattern. The control caters really well to data changes, by not storing the data that is to be displayed, but rather asking for it when it is needed.
The problem with UITableView
really lies in displaying static content – content that largely doesn’t change, and content which is known to you at development time. Good examples of this would be any form of navigation, such as the hierarchical navigation structure in Settings.app, or an interface that is fixed, such as the detail view for a single contact in Contacts.app.
For these kind of interfaces, although you may want the table view look and feel, implementing the delegate/data source methods often feels very verbose for how you are using it.
I really like the article Fraser Speirs has written to counter the problems associated with UITableView
in such a scenario. The idea is essentially to create enumerations for the sections in the table view, and the rows in each section. This provides constants as mappings between row and section indexes, and the data you want to display. What’s more is that each enumeration is ended with a constant which can be used for the number of sections/rows in that enumeration, which works on the fact that the enumeration is 0-based. Here is an example:
enum Sections { kHeaderSection = 0, kTitleSection, kAuthorSection, kBodySection, NUM_SECTIONS };
Now you can return NUM_SECTIONS
in-numberOfSectionsInTableView:
, which will always be the number of sections in the enumeration, and you can use switch
statements in row or section-dependent methods such as -tableView:cellForRowAtIndexPath:
with these constants, rather than having to hard-code row and section numbers.
Although much more resistant to future change and more structured, the problem with this method is that your code quickly devolves into a mess of switch statements, which doesn’t make the code much less verbose.
Solving the problem with PXSimpleTableAdapter
My primary motivation for PXSimpleTableAdapter
was to overcome this problem of verbosity, and make setup and getting information into a table view really easy.
Why did I name it “Simple” table adapter? Because the classes are designed for only this use case – where you have “static” data. If you have dynamic data, UITableViewDelegate
and UITableViewDataSource
are the way to go.
Get the source
As with all of my open source projects, you can clone
the project straight from GitHub:
git clone git://github.com/Perspx/PXSimpleTableAdapter.git
Or have a look at the project page at http://github.com/Perspx/PXSimpleTableAdapter.
Classes
The structure of the classes is simple:
PXSimpleTableAdapter
: This is the class which transforms the data you pass it into cells that can be used by aUITableView
, as well as handling selection.PXSimpleTableSection
: This class encapsulates information about a section in the table, including the rows it contains.PXSimpleTableRow
: This class encapsulates information about a row in the table, such as its title and icon.
Using PXSimpleTableAdapter
is very straighforward:
- Lay out a
UITableView
in Interface Builder. - Create an instance of
PXSimpleTableAdapter
in your view controller class and set thetableView
property to your table view instance. This property is declared as an outlet so this step can also be done in IB, by dragging out an Object from the Object Library for your table adapter instance then hooking this up to your view controller as another outlet). - Create an array of
PXSimpleTableSection
instances, and set each section’s array ofPXSimpleTableRow
instances. - Pass the array of sections to
-setSections:
on thePXSimpleTableAdapter
instance.
Convenience methods can be added if sections or rows need to be added or removed. Have a look at the PXSimpleTableSection
and PXSimpleTableRow
header files for more information.
Data Model
Each PXSimpleTableSection
instance encapsulates:
- The header title for the section
- The footer title for the section
- The array of rows for that section
Each PXSimpleTableRow
instance encapsulates:
- The title of the row
- The icon for the row (if needed)
- The accessory type (from the
UITableViewAccessoryType
enumeration)
Rows also have a selection block and an accessory tapped block property. These blocks are invoked when the row is selected, or their accessory button is tapped, respectively.
Example
Here is an example of creating a view which looks similar to that of Settings.app (if using a table view with the grouped style):
NSArray *firstSectionRows = [NSArray arrayWithObjects:[PXSimpleTableRow rowWithTitle:@"Sounds"], [PXSimpleTableRow rowWithTitle:@"Brightness"], [PXSimpleTableRow rowWithTitle:@"Wallpaper"], nil]; PXSimpleTableSection *firstSection = [[PXSimpleTableSection alloc] initWithSectionHeaderTitle:nil sectionFooterTitle:nil rows:firstSectionRows]; NSArray *secondSectionRows = [NSArray arrayWithObjects:[PXSimpleTableRow rowWithTitle:@"General"], [PXSimpleTableRow rowWithTitle:@"Mail, Contacts, Calendars"], [PXSimpleTableRow rowWithTitle:@"Phone"], nil]; PXSimpleTableSection *secondSection = [[PXSimpleTableSection alloc] initWithSectionHeaderTitle:nil sectionFooterTitle:nil rows:secondSectionRows]; NSArray *sections = [[NSArray alloc] initWithObjects:firstSection, secondSection, nil]; [firstSection release]; [secondSection release]; PXSimpleTableAdapter *tableAdapter = ... [tableAdapter setSections:sections]; [sections release];
For such static content, this is much more manageable, and the code to set up the view resides in one method only, rather than 5 or so delegate messages with their own conditionals.
Custom Cells
By default, the data encapsulated in each PXSimpleTableRow
is transformed to a UITableViewCell
with the default style. This cell is then passed to the table view for display.
To use a custom cell in rows, you must subclass PXSimpleTableRow
where you can provide your own cells and set them up as necessary. You must create one PXSimpleTableRow
subclass for each type of cell you are going to be using.
There is more information on how to do this in the project README.
But what about delegation?
Delegation is one of my favourite patterns used in Cocoa and it really is powerful. The aim of this project is not to break delegation or suggest that this is the way UITableView
should go.
However, the aim of PXSimpleTableAdapter
is simply as a convenience for implementing this particular use case of table views in a simple manner.
Support/Bugs
If you have any bugs to report then you can do so on the GitHub project page.
If you have any queries about the project, first check out the project README on GitHub, or the Demo App bundled with the source. If your question still remains unanswered, you can contact me here. I’ll do my best to reply to any queries if I can.
Comments — 2
Mar 29, 2011
This looks really useful – thanks!
Aug 10, 2011
As an iOS newbie, this is just what I was looking for. Thanks!
Add comment