1211 Pro iOS Table Views
Chapter 5, “Using Tables for Navigation,” covers an almost-ubiquitous feature of the iOSuser interface, and shows how tables can be used to navigate through a hierarchy of data in a simple and consistent way. In Chapter 8, “Improving the Look of Cells,” you will start to look at the process of going beyond standard cell types to customize the look and feel of your table views.
The Style of This Book
Although that’s the quickest way to get up and running, I encourage you to take the extra time to key in the code yourself as you go along. Where to Find Out MoreBeyond the pages of this book, there’s a wealth of other information available online (not to mention the great range of other Apress titles): ■ For a general overview, Apple’s “Table View Programming Guide for iOS” is adetailed guide that covers most of the topics in this book.
Some basic table-based applications At the other end of the scale, the default look, feel, and behavior of the table view and cells can be customized to the point where they are hardly recognizable as table viewsat all. The basic anatomy of a table view Table view operations are supported by twoUITableView protocols: UITableViewDatasource provides the table view with the data that it needs to construct and configure itself, as well as providing the cellsthat the table view displays.
Creating a Simple Table View App
Okay—you’re going to do the following:Create a simple, window-based application skeletonGenerate some data for feeding the table Wire up the table view’s data source and delegateImplement some very simple interactivity It’s all very straightforward but useful practice. Creating the Application Skeleton For this application, you’re going to use a simple structure: a single view managed by a view controller, and a NIB file to provide the content for the view.
CMAppDelegate. This prevents any chance of you inadvertently creating classes that clash with existing ones, either in the iOS SDK or any libraries that you might be using
When you’ve reached this point, you’ll see the project view of Xcode, with the initial skeleton of your application. The initial Xcode view showing our new skeleton application You’ll see that you have the following:An app delegate ( STAppDelegate.h and .m)A table-view controller ( STViewController.h and .m)A NIB file containing the view ( STViewController.xib) At the end of this chapter, you’ll look again at how these fit together.
NSMutableArray of NSStrings that contains some information to go into each cell
The data array will need to be ready by the time the data source is called by the table view—so where to create it? Arrow key combination, or by clicking the filename in the breadcrumb display at the top of the code editor then selecting the file from the drop-down menu that appears.
A NOTE ABOUT MEMORY MANAGEMENT IN IOS 5
Click the STViewController.xib file in the project explorer, and you’ll see the NIB file open in the Interface Builder pane, as shown in Figure 1–9. Editing the NIB file in Interface Builder In the Objects browser at the bottom right, find the Table View item and drag it out onto the view in the center.
NOTE: Data sources and delegates are covered in detail in Chapters 2 and 3. Conforming the Class to theUITableDelegate and UITableDataSource Protocols #import <UIKit/UIKit.h> @interface STViewController : UIViewController <UITableViewDelegate,UITableViewDataSource> @property (nonatomic, strong) NSMutableArray *tableData;@property (nonatomic) int cellCount; @end This will tell the compiler to expect the required methods to have been implemented.
Wiring Up the Data Source and Delegate
Our STViewController object will be ready shortly, but the table view itself doesn’t yet know that it will act as both a data source and a delegate. Right-click the table-view object (either in the main Interface Builder or in the objects tree in the middle pane), and you’ll see the Table View property “head-up display”(HUD), as shown in Figure 1–12.
To connect these, mouse-over the circle to the right of the dataSource entry. The circle
The Table View properties HUD will now show that both the dataSource and the delegate are connected to the File’s Owner, as shown in Figure 1–14. ThedataSource and delegate connected to File’s Owner File’s Owner in this context is the view controller for the view, our STViewController object.
Displaying the Data
If you pull down the breadcrumb menu at the topof the edit pane, you’ll see that this line breaks up the list of methods and makes the code easier to navigate. So you can configure the contents and return it to the calling method with thefollowing: cell.textLabel.text = [self.tableData objectAtIndex:indexPath.row]; return cell; At this stage, you have some data and a table view, and you have wired up the methods that feed the table view with the data.
If the delegate implements the tableView:didSelectRowAtIndexPath: method, it can use
For example, in the iPhone’s Contacts app, a view showing the contact’s details will be displayed. When a row is tapped, you’ll log the event into the debugger, and you’ll pop up a modal dialog box showing whichrow has been tapped.
Understanding How the App’s Objects Fit Together
This in turn has a view outlet, which is connected to the view object in the NIB file. Summary In this chapter, you’ve created a very basic table view stage by stage: To start, you created some data to display in the table.
UITableViewDelegate protocols so that it could provide the data for the table, and the response to interaction
You implemented the code required to create cells for the table. From here, it’s time to look in much more detail at how tables and cells are constructed, together with how they can be customized and made to respond to user interaction.
Along the way, you’ll look at the following: The types and styles of table viewsThe anatomy and dimensions of table viewsUITableView’s relationship to the UIScrollView superclassCreation of table views in code and with Interface BuilderUse of the UITableViewController class to take advantage of its template methods Understanding Table Views At its simplest, a table view is a list of items that can (often) be scrolled vertically. Because this is a common interface design pattern, UIKit provides a powerful suite of classes and protocols to make the creation and management of table views as simpleand as effective as possible.
Choosing the Type of Table View Although their visual appearance can be customized to the point where it’s almost difficult to recognize them as instances of the UITableView class at all, table views comein one of two basic forms: plain and grouped. A plain table view and its components The plain table is the version that is created by default, although the type can be specified: UITableView *tableView = [[UITableView alloc] initWithFrame:tvFrame style:UITableViewStylePlain]; If the number of rows in the tableView means that the table will scroll to reveal more rows, scroll indicators appear in the right-hand scroll area when the table is in motion.
The Indexed Table
UITableViewDataSource has two methods to support this: If for any reason you need to find the dimensions of the headers and footers, these are returned (asCGRect values) by the following: The dimensions of the entire section (header, footer, and content) are available by calling the following: 32 Setting TableView Dimensions One way to visualize a tableView is to think of it as a window that provides a view of a conveyor belt of cells. (In fact, unless the table is small, this very rarely happens because ofUITableView’s caching and queuing mechanism.) contentSize is calculated by adding the total height of all the rows, plus the header and footer views.contentOffset indicates how far down the table has been scrolled from the top of the tableView’s frame.
One of the main parts of building tableViews is getting the objects to play nicely
together, so this chapter covers the following:Where the table gets its data, and how you get it thereAn initial look at how the table handles interactionHow the table keeps track of sections and cellsAn overview of the architecture patterns that the UITableView classes exploitSome of this chapter’s content might feel somewhat abstract and theoretical—but sticking with it is worthwhile. (It was objectB that setitself as objectA’s delegate, remember, so the onus is on objectB to clean up after itself, 48 so to speak.) The best place to do this is within objectB’s dealloc method, because thisis the last thing called before the object winks out of existence: Although sending a message to nil won’t actually do anything—the message is simply discarded—at least it won’t cause your program to fall over completely.
TheUITableViewDataSource Protocol @protocol UITableViewDataSource<NSObject> @required This tells us that although the UITableViewDataSourceProtocol defines a myriad of methods, only two of them have to be implemented in order for the table to function. A tableView gets around this by assuming that unless the dataSource says otherwise, the table view has just the one section.
Using UITableView’s Delegate Methods
UITableView uses the delegate pattern to obtain data from its dataSource and to handle user interaction and table configuration (see Figure 3–4). Despite the name, atableView’s dataSource is a form of a delegate—just one with specific responsibilities.
Configuring the tableView’s rows
Configuring the header and footer of sectionsManaging selection of rowsEditing rowsManaging accessory viewsReordering rows Table 3–1 shows the methods available in the UITableViewDelegate protocol. This is a commonly implemented method that is often used to trigger the appearance ofanother view controller, or to toggle a check mark in the cell.
The Key Information Required By a UITableView
A UITableView needs three key pieces of information in order to successfully draw itself and its cells:The number of sections in the tableThe number of rows in the section The cells that belong in the rows within the sections. How the Key Information Is Obtained by the Table The conversation between the four objects involved – the view controller that contains thetableView, the tableView itself, and the delegate and dataSource objects belonging to thetableView – takes place in a specific order.
The Thing to Bear in Mind About dataSource Methods
58 UITableView has three sets of methods that can be used for this – reloadData reloads the whole table, while reloadRowsAtIndexPath updates specific rows.reloadSectionIndexTitles and reloadSections:withRowAnimation: update the specified sections. Because D is the fourth letter of the alphabet, the elements beginning with D appear in section 3(remember, indexPath numbering, as with NSArrays and so on, starts at 0).
The Model-View-Controller Design Pattern To the untutored eye, an iOS application opened in Xcode looks like a mess of code
(Okay, the analogy breaks down slightly if you’re thinking about the latest Pixar blockbuster, but animated characters still 61 need voices, right?) They’re the views, responsible for presenting the data of the script to the audience (via the medium of the camera, of course). If your application’s logic was embedded in the views, you’d need to create it twice—once for the smaller screenof the iPhone, and again for the bigger screen of the iPad.
MVC and iOS
The key to staying sane with MVC in the iOS world is to remember that MVC is a conceptual framework, rather than something more absolute, such as a set ofdirectories. If you bear in mind that the table’s data comes from a model, and that model is actually the NSArray I mentioned, you’ll still be thinking (and working) in an MVC way.
MVC and tableViews
The data that is displayed by the tableView isprovided by the dataSource, while the user’s interaction with the tableView is handled by the delegate. Both of these are examples of the delegation design pattern, while thedivision of labor between the tableView, its controllers, and the underlying data is themodel-view-controller architecture pattern in action.
UITableViewDataSource and UITableViewDelegate. Building a tableView is a process of implementing (at least) the required methods, and often some of the optional ones as
Armed with an understanding of where the table data comes from, and how the table handles interactions, you’re ready to dive into the details and start the process ofcustomization. You’ll see the following: The internal structure of a cellThe standard cell types that come for free with UITableViewThe configuration of default cell contentThe use of accessory viewsThe creation and reuse of cells This covers everything you need to know in order to create default-styled cells and perform basic configuration of them.
Basic Structure of the Cell
The basic layout of a cell in normal mode The content of the cell can be accessed en masse through the contentView property, which is a UIView. When switched to editing mode, the cell’s contentView is reduced in width by about 40 points, and an editing control is inserted at the left side, as shown in Figure 4–3.
UITableViewCellStyleDefault The content of the textLabel is accessed through its text property: cell.textLabel.text = @"textLabel"; Similarly, the image is set by accessing the imageView’s image property: cell.imageView.image = [UIImage imageNamed:@"panda"];Using UITableViewCellStyleValue1 TheValue1 cell style is similar to the Default type, but includes an extra, optional UILabel called detailTextLabel and an optional imageView. Truncation of cell content 67 Using UITableViewCellStyleValue2 The Value2 cell style is virtually identical to the Value1 style, with the exception that the default weight of the textLabel and detailTextLabel are altered, and there’s noimageView.
Cells themselves don’t have state. Remember, even in a tableView of 99,999 rows, only
Similarly, if you set the accessory view state of a cell that is then recycled from the cache, it’ll arrive back at the table in the same state as it had when the cell was dumpedinto the cache. As the cells are popped and pushed in and out of the cache, the cellForRowAtIndexPathmethod checks whether the peer exists in the listOfPlayers array.
Speed and Smoothness
Behind the scenes,the dataSource uses this cellIdentifier to do the following: Drop the discarded cell into the right queue for later reuseRetrieve a cell of the right type from a queue when the tableView requests a new cell Figure 4–16 shows a somewhat contrived example with two cell types alternating on odd and even rows. There are three places where you can amend cell content: cellForRowAtIndexPath: As we’ve already seen, this is where you’ll domost of the cell’s configuration—setting content items based on the data returned by the tableView’s model and so on.prepareForReuse: This method gets called on the cell in the background just before it’s returned to the delegate bydequeueReusableCellWithIdentifier.
Summary In this chapter, you’ve looked in depth at how cells are structured, created, and reused
They enable a user to manage the navigation through a hierarchy of content, moving throughthe tree of content items in a simple and consistent way. The built-in iPhone Contacts app This user interface pattern is so common that the iOS SDK provides a controller for this specific purpose that handles the heavy lifting of the navigation for you.
The Navigation Controller Interface Pattern
The components of UINavigationController Interacting with the content inside the view controllers—tapping on a row, tapping on a button, and so on—is the cue to call theUINavigationController’s pushViewController:animated: and popViewController:animated: methods. You’re not restricted to using a table at the top level; you could just as easily push in the next view by tapping on say, a UIButton, as you could by tapping on a row in a tableView.
UINavigationController really calls for an example application that is a bit more complex than the SimpleTable app that we’ve been using as our example so far. To do this, I’ve
Creating a Navigation Controller App
If you’ve got kids (or even if you haven’t and you‘ve got friends who have) you’ll know that one of the most important decisions you can make before the little bundle of joy arrives is deciding on a name. Your version of Xcode may look different from this, but among the templates there’ll be one that creates a skeleton application with a single view; that’s the one we’re after.
So core, in fact, that you’re going to create a BNName class with the following attributes (see also Figure 5–7).nameText - A string containing the name itself gender - A string containing a gender flag M, F, or U (for unisex)derivation - A string containing some text about the derivation of the nameiconName - A string containing the filename of the name’s icon notes - A string containing some explanatory notes about the name 90 Figure 5–7. In order to work with these,you have to tell the view controller about the BNName class, so add a #import statement to the top of the file: #import "BNName.h" And now you can update the tableView:cellForRowAtIndexPath: method as shown in Listing 5–10.
Building the Detail View
Switch back toBNViewController’s implementation file, find theviewDidLoad method, and add this line to the bottom: self.title = @"Baby Names"; 107 If you run the app now, you’ll see that the table is still there, but now it sits inside a navigation controller that provides a top bar. TheviewDidAppear: Method First, you need to get hold of the indexPath of the currently selected row (which is the same as it was when the detail view was pushed in), and then use this to call thetableView’s deselectRowAtIndexPath:animated: method.
UINavigationController-based app from scratch. The app delegate loads the navigation controller. The navigation controller
Having put the structure and the basic function of the app together, you can adapt this to drive the table with any suitable model as the data source. The structure of that datawill determine how you’ll handle drilling into the detail, and using the techniques that are explored in Chapter 8 onwards, you can customize the look and feel of the table tomatch your app’s design.
Using Indexed Tables
The ActualnumberOfRowsInSection Method Having established the number of rows in each section, and creating a cell for each row, there are four things you need to do to get the indexing side of things working:Return the number of sections in the table. TheSectionIndexTitlesForTableView Method Matching the Index to the Section When an element in the index is tapped, the tableView will automatically scroll so that the heading for the corresponding section is at the very top of the table.
Building Practical Sectioned Tables
The simple table that you’ve built so far will hopefully have given you a feel for how an indexed and sectioned table fits together—but it was a very simple example. In this section, you’re going to build a more complex example with data that can support the table types in Figure 6–6.
Creating the Data for a Table with Sections and Indexes
There are two ways to create the data for an indexed table: Manually, by creating an array of arrays yourselfUsing the UILocalizedIndexedCollation class to do much of the heavy lifting for youWhich method you use is a matter of personal preference and the dictates of the app you’re building, so I’ll cover both. There’s an implicit assumption here that you’ll be adding the objects into the arrays (and the inner arrays into the outer array) in the order that you want them to appear in thetable.
NSArray *innerArrayE = [NSArray arrayWithObjects:@"E1", @"E2", @"E3", @"E4", nil];
In each case, UILocalizedIndexCollation will use the relevant locale to figure out how the row objects should be organized and sorted—which means you don’t need to knowyour S from your ß… A the name suggests, theUILocalizedIndexedCollation class handles a lot of the heavy lifting involved in localizing your app, and is dependent on the localization settings of your application bundle. If you’re using one of the Ø Scandinavian locales, however, you’ll also automatically get an entry for the character—so the class can save a lot of time and effort.iOS localization is even clever enough to support non-Latin character sets.
Creating Some Data in a plist File
Add the following to the header file, and then the corresponding synthesis in the implementation: @property (nonatomic, strong) UILocalizedIndexedCollation *collation; And instantiate it in the viewDidLoad method back in the implementation file: self.collation = [UILocalizedIndexedCollation currentCollation]; Because there’s a fair amount of code involved to set up theUILocalizedIndexedCollation, you’ll put this in its own method. This is the count of the number of elements in the appropriate inner array, sothe first step is to get a reference to the array that is the nth object in the outer array(where n is the section number).
Finding the Current Scroll Position in the Table
For example, the current (at the time of writing) version of the Path app displaysa timestamp alongside the right-hand end of a table that slides up and down to indicate how far down the timeline you’ve gone (see Figure 6–18). Figuring out how tall the table is going to be is a little trickier, mainly because of the way that the table view handles building rows and loading data without the need for much“manual” intervention on your part when writing the code.
In this chapter, you have looked at a couple of methods to improve the visual presentation of large amounts of data in table views. Breaking the data—and the table—into sections provides additional structure to the table, while indexes provide a means of quick navigation between sections.
In this chapter, we’ll look atHow to handle selection of rows in your tablesHow tables can be built to handle rearrangement of their dataHow tables can be used to create, edit, update, and delete items from their underlying data models A Recap of the Model-View-Controller Pattern Before getting into the mechanics of selecting, inserting, and deleting with a table view, you’ll need to understand how these changes affect the underlying data that the tabledisplays. In other words, the controller is the class or classes that control the display ofdata and interaction with the table—either because it’s an entire subclass of UITableViewController, or because it’s a UITableViewController delegate and/or data source.
Why the Model-View-Controller Pattern Is Important
To the user, this will seem as if the state of the cell isn’t “sticky.” If the user taps a row to set a value and display a check mark, the selection might change if the user scrolls up ordown. The tableView handles revealing the edit controls, removing the deletedrow, and animating the "closing of ranks" as the rows move up to fill the empty space.
UITableView has an allowsSelection property, which can be set either in Interface
Builder as you construct the view or programmatically at any point in the table’s life cycle. Setting the allowsSelection property to NO disables row selection completely, sothe table won’t react to touches (other than scrolling).