programming4us
programming4us
MOBILE

iPhone Programming : Creating a Table View

- How To Install Windows Server 2012 On VirtualBox
- How To Bypass Torrent Connection Blocking By Your ISP
- How To Install Actual Facebook App On Kindle Fire
10/10/2010 5:28:47 PM
With refactoring out of the way, now it’s time to put the UI together.

Double-click the RootController.xib file in Xcode to open it in Interface Builder. Then double-click on the View icon in the RootController.xib window to bring up the View window, and drag a table view from the Library window into the view. You’ll find the table view under Cocoa TouchData Views in the Library window.

Center the UITableView in the view, as shown in Figure 1. You must confirm that you’ve dropped it as a subview of the main view by clicking the View Mode widget on the menu bar of the RootController.xib window and choosing List View. It should look as shown in Figure 1, with Table View appearing under View. Save the .xib file using ⌘-S.

Figure 1. Dragging a UITableView from the Library window into the UIView


Switch back to Xcode to add the outlets and delegates Interface Builder needs so that you can connect the UITableView to your code. Open the RootController.hUITableView variable to the @interface declaration, then declare this as a property and an IBOutlet. You also need to declare that this class implements both the UITableViewDataSource and the UITableViewDelegate protocols. This means that it both provides the data to populate the table view and handles events generated by user interaction with the table view. interface file and add a

Once you’ve done this, the RootController.h file will look like this:

#import <UIKit/UIKit.h>

@interface RootController: UIViewController
<UITableViewDataSource, UITableViewDelegate>
{
UITableView *tableView;
}

@property (nonatomic, retain) IBOutlet UITableView *tableView;

@end

If you Option-double-click UITableViewDataSource in the declaration and then click the documentation icon in the upper-right corner of the window that appears (or ⌘-Option-double-click to go directly there), you’ll see that the protocol has a number of optional methods, as well as two mandatory methods (you must implement the methods that aren’t labeled as “optional”). Having declared that our view controller is a UITableViewDataSource, our RootController implementation must implement these two mandatory methods. These methods are tableView:cellForRowAtIndexPath: and tableView:numberOfRowsInSection:. The first of these methods returns a UITableViewCell object; the table view will ask the data source delegate for a cell each time a new cell is displayed in the view. The second method returns an NSInteger determining how many sections are in the table view. Table views can be divided into sections, and a title added to the top of each section. For now, we’ll use just one section (the default).

Despite what the documentation for UITableViewDelegate seems to suggest, there aren’t any mandatory methods. However, to obtain any sort of functionality from our table view we will at least have to implement the tableView:didSelectRowAtIndexPath: method.

Now we must add the implementation of those two mandatory data source methods to the RootController class (RootController.m). Once we have the code up and running we’ll look at the tableView:cellForRowAtIndexPath: method in detail. This method returns a populated table view cell for each entry (index) in the table, and it’s called each time the view controller wants to display a table view cell. For example, it’s called as the table view is scrolled and a new cell appears in the view.

Here are the contents of RootController.m. I marked in bold the lines I added to the file that the Xcode template generated:

#import "RootController.h"

@implementation RootController

@synthesize tableView;
#pragma mark Instance Methods

(void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}

- (void)dealloc {
[tableView release];
[super dealloc];
}

#pragma mark UITableViewDataSource Methods
- (UITableViewCell *)tableView:(UITableView *)tv
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell =
[tv dequeueReusableCellWithIdentifier:@"cell"];
if( nil == cell ) {
cell = [[[UITableViewCell alloc]
initWithFrame:CGRectZero reuseIdentifier:@"cell"] autorelease];
}
return cell;
}
- (NSInteger)tableView:(UITableView *)tv
numberOfRowsInSection:(NSInteger)section
{
// Our table view will consist of only 3 cells
return 3;
}
#pragma mark UITableViewDelegate Methods

@end


1. Organizing and Navigating Your Source Code

I introduced something new in the preceding code listing: the #pragma mark declaration. If you examine the lower-righthand pane of the Xcode interface you’ll see that the title bar contains a filename, and immediately to the right of this is the name of the method inside which your cursor currently happens to be. If you click on this, you’ll see a drop-down menu showing all the method names in the implementation (you can access this menu easily using the Ctrl-2 keyboard shortcut). You’ll also see the text of the pragma marks I added to the code. For large classes, this is a convenient way to separate the methods involved in different jobs. In this case, I’ve added marks for the instance, data source, and delegate methods. You can also add a horizontal bar to the method list by adding the following:

#pragma mark -

Do not add a space after the -, as this will make Xcode think this is a text comment.

2. Connecting the Outlets

Open the RootController.xib file, and when Interface Builder opens, set the RootController.xib main window’s view mode to List, and then open the View list to reveal the table view.

Next, click the Table View icon and set the Inspector window to display the Connections Inspector (⌘-2). This reveals the dataSource and delegate outlets. Connect both of these to File’s Owner in the main window, which in this case is the RootController class, as shown in Figure 2.

Figure 2. Connecting the dataSource and delegate outlets of the UITableView in Interface Builder to the RootController class (File’s Owner)


Now click on the File’s Owner icon. In the outlets section of the Connections Inspector (⌘-2) you’ll see the tableView object that we flagged as an IBOutlet in the RootController.h file. Connect this with the UITableView as shown in Figure 3.


Note: If you don’t see the tableView object, quit Interface Builder (save your work so far), return to Xcode, and make sure you saved RootController.h. Then open RootController.xib in Interface Builder again; it should appear when you select the File’s Owner icon and go to the Connections Inspector.
Figure 3. Connecting the tableView IBOutlet in the RootController to the UITableView subview


We’ve reached a natural point at which to take a break. Quit Interface Builder (be sure to save any changes) and return to Xcode. The code should now run without crashing, although it’s not going to do very much. So, click Build and Run (or Build and Debug) to start the application in iPhone Simulator. Figure 4 shows what you should see.

OK, now we have the basic table view code working, so let’s go back to the RootController implementation (RootController.m) and look at that tableView:cellForRowAtIndexPath: method where we were creating and then returning table view cells. For performance reasons, the UITableView can reuse cells to enhance scroll performance by minimizing the need to allocate memory during scrolling. However, to take advantage of this ability we need to specify a reuse identifier string. The UITableView uses this to look up existing cells with the same identifier using the dequeueReusableCellWithIdentifier: method. If it can’t find an unused cell with the correct identifier, it will create one, but if an unused cell is available (perhaps it’s scrolled out of the current view), it will reuse it:

UITableViewCell *cell =
[tv dequeueReusableCellWithIdentifier:@"cell"];
if( nil == cell ) {
cell = [[[UITableViewCell alloc]
initWithFrame:CGRectZero reuseIdentifier:@"cell"] autorelease];
}
return cell;

Figure 4. The empty table view inside iPhone Simulator


So far our table view isn’t that interesting, so let’s push forward and add some content and some event handling. To do this, add an implementation for the tableView:didSelectRowAtIndexPath: delegate method to RootController.m. As the name suggests, this method is called when a user clicks on a table view cell. Because our cells are empty at the moment, we’ll also add some text to a cell before returning it from this method. Added lines of code are shown in bold:

#pragma mark UITableViewDataSource Methods

- (UITableViewCell *)tableView:(UITableView *)tv
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell =
[tv dequeueReusableCellWithIdentifier:@"cell"];
if( nil == cell ) {
cell = [[[UITableViewCell alloc]
initWithFrame:CGRectZero reuseIdentifier:@"cell"] autorelease];
}

cell.textLabel.text = @"Testing";
return cell;
}

- (NSInteger)tableView:(UITableView *)tv
numberOfRowsInSection:(NSInteger)section
{
// Our table view will consist of only 3 cells
return 3;
}

#pragma mark UITableViewDelegate Methods

- (void)tableView:(UITableView *)tv
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tv deselectRowAtIndexPath:indexPath animated:YES];
}


You can see the results of these additions in Figure 5.

Figure 5. The new code running inside iPhone Simulator


Other  
 
programming4us
 
 
programming4us