Almost every application using a table associates that table with a navigation bar. Moreover, in applications of any complexity, the application also usually organizes its tasks into different tabs. For instance, the iPod application has different tabs for different views of its data. The Artists tab, for instance, shows a user’s multimedia sorted by artist. At the iPod application’s top is a navigation bar. When a user selects an artist row, the navigation controller pushes another view onto the navigation controller and displays it. In the following Try This, I try to instill some real-world credibility to this chapter by having you implement a table in a navigation controller that is in a tab-bar tab. Although initially confusing, this is such a common user interface pattern that you should definitely understand the next task. Using a Table in a Navigation Controller in a Tab This is a long, but useful task. Much of it is repetition from previous chapters, but the combination of a table in a navigation controller in a tab is such a common application pattern that it is worth presenting here in detail, even if much of the task is repetitive. Creating and Connecting the Views
Figure 10-23 Navigation controller in place of tab’s view controller
Figure 10-24 Application with a table, navigation bar, and tab bar
NOTE In step 14 you are adding the table directly to the nib as the main view. Contrast this with how you added the table view in this chapter’s first Try This example. Both techniques work.
Figure 10-25 Table in a tab bar
Listing 10-23 TabNavTableAppDelegate.h #import <UIKit/UIKit.h> @interface TabNavTableAppDelegate : NSObject <UIApplicationDelegate> { UIWindow *window; IBOutlet UITabBarController * myCont; } @property (nonatomic, retain) IBOutlet UIWindow *window; @property (nonatomic, retain) IBOutlet UITabBarController * myCont; @end Listing 10-24 TabNavTableAppDelegate.m #import "TabNavTableAppDelegate.h" @implementation TabNavTableAppDelegate @synthesize window; @synthesize myCont;(void)applicationDidFinishLaunching:(UIApplication *)application { [window addSubview: view]; [window makeKeyAndVisible]; }(void)dealloc { [myCont release]; [window release]; [super dealloc]; } @end Listing 10-25 MyTableViewController.h #import <UIKit/UIKit.h> @interface MyTableViewController : UITableViewController { NSMutableArray * tableDataList; } @property (nonatomic, retain) NSMutableArray * tableDataList; @end Listing 10-26 MyTableViewController.m #import "MyTableViewController.h" @implementation MyTableViewController @synthesize tableDataList; - (void) viewDidLoad { NSMutableArray * tempArray = [[[NSMutableArray alloc] initWithObjects:@"Item One", @"Item Two", @"Item Three", @"Item Four", @"Item Five", @"Item Six", @"Item Seven", @"Item Eight", @"Item Nine", @"Item Ten", @"Item Eleven", @"Item Twelve", @"Item Thirteen", @"Item Fourteen", @"Item Fifteen", @"Item Sixteen", @"Item Seventeen", @"Item Eighteen", @"Item Nineteen", @"Item Twenty", nil] autorelease]; self.tableDataList = tempArray; } (NSInteger) tableView : (UITableView *) tableView numberOfRowsInSection: (NSInteger) section { return [self.tableDataList count]; }(UITableViewCell *) tableView : (UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *) indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"acell"]; if(cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:@"acell"] autorelease]; } cell.textLabel.text = [self.tableDataList objectAtIndex:[indexPath row]]; return cell; } - (void)dealloc { [tableDataList release]; [super dealloc]; } @end Handling Row Selections
Figure 10-26 Clicking the row takes the user to details view.
Listing 10-27 TableViewDetailsViewController.h #import <Foundation/Foundation.h> @interface TableViewDetailsViewController : UIViewController { } @end Listing 10-28 TableViewDetailsViewController.m #import "TableViewDetailsViewController.h" @implementation TableViewDetailsViewController @end Listing 10-29 The tableView:didSelectRowAtIndexPath: method added to MyTableViewController - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath { NSLog(@"pushing..."); TableViewDetailsViewController * temp = [[ [TableViewDetailsViewController alloc] initWithNibName: @"TableViewDetailsViewController" bundle:nil] autorelease]; [self.navCont pushViewController:temp animated:YES]; }
Table cells can be edited. You can add new rows, delete existing rows, and reorder rows. The way it works is like this: A user clicks a button that puts the table into edit mode. Edit mode displays insert or delete accessories used for adding and deleting rows. These accessories are displayed on a cell’s left side. Editing mode displays reorder accessories on a table cell’s right side.
This chapter ends by discussing how to edit a table. Tables not only display data, they also allow adding rows, deleting rows, and changing the order of rows. A table has two modes: its normal display mode and edit mode. When a table is in edit mode, it can display accessories for inserting, deleting, and rearranging table cells. An application places a table in edit mode by sending a message to the table view’s setEditing:animated: method. For instance, you might call a table’s setEditing:animated: method by implementing an IBAction called by a button on a form. (IBAction) edit { [self.myTableView setEditing:YES animated:YES]; } However, a self-created button is not how tables are usually placed into edit mode. Rather than specifically creating an action and manually calling a method, you usually use a navigation item and let it automatically activate a table view’s edit mode. Remember, 90 percent of the time you will implement a table view in a navigation controller. That navigation controller’s navigation bar can contain a right button. One choice you have when creating a navigation bar button is creating an Edit button. self.navigationItem.rightBarButtonItem = myTableController. editButtonItem; When you set that button to a table controller’s editButtonItem, the controller automatically knows to enter edit mode.
tableView:canEditRowAtIndexPath:,tableView:canMoveRowAtIndexPath:,tableView:commitEditing Style:forRowAtIndexPath:,and tableView:commitEditingStyle: forRowAtIndexPath: are four methodsyou should implement in your UITableViewDataSource protocol adoptee. (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath: (NSIndexPath *)indexPath (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath: (NSIndexPath *)indexPath (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle) editingStyle forRowAtIndexPath: (NSIndexPath *) indexPath (UITableViewCellEditingStyle)tableView:(UITableView *) tableView editingStyleForRowAtIndexPath:(NSIndexPath *) indexPath A table knows a row is editable by the tableView:canEditRowAtIndexPath: method. If you wish all rows to be editable, simply have the method return YES; otherwise, implement code to determine if a particular row is editable. If you omit this method, no rows are editable. The tableView:editingStyleForRowAtIndexPath: method informs the table what style editing accessory the row should have. If this method returns a UITableViewCellEditingStyleNone, Figure 10-27 A table where the first and last rows have no editing style,no accessory is displayed. If this method returns UITableViewCellEditingStyleDelete, the delete accessory is displayed. And if the method returns UITableViewCellEditingStyleInsert, the insert accessory is displayed. The following example code in Listing 10-30 and Figure 10-27 illustrates. Listing 10-30 The tableView:editingStyleForRowAtIndexPath method - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { if(indexPath.row == 0 || indexPath.row == [self.workoutArray count]- 1) { return UITableViewCellEditingStyleNone; } return UITableViewCellEditingStyleDelete; } A table knows a table row is movable by the tableView:canMoveRowAtIndexPath: method. Like the tableView:canEditRowAtIndexPath: method, simply have the method return YES if all rows are moveable; otherwise, write your own custom code. If you omit this method, no rows are movable.The tableView:canMoveRowAtIndexPath: method only prevents particular rows from being directly moved by a user. A user can still move another row to the position held by the unmovable row, thus moving the unmovable row indirectly. To prevent this behavior,you can implement the tableView:targetIndexPathForMoveFromRowAtIndexPath: toProposedIndexPath: method in your table view’s delegate. If a proposed move is acceptable, return the proposedDestinationIndexPath; otherwise, return the sourceIndexPath. The method’s signature follows. - (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath The tableView:commitEditingStyle:forRowAtIndexPath: commits a row insertion or deletion. Notice the editingStyle parameter. If a row is deleted, the table view sends a UITable ViewCellEditingStyleDelete style as the editingStyle parameter. If a row is inserted, the table view sends a UITableViewCellEditingStyleInsert style. It uses the editingStyle parameter to tell it which action resulted in the method being called. This method is for implementing code that handles a row insertion or deletion. For instance, in this chapter, data comes from an NSArray or NSMutableArray. When using an NSMutableArray as a data source for a table, deleting a row means you must delete the corresponding item from the array. If inserting, you must add an item at the index where the row was inserted and remove the item from its old location in the array.
its editButtonItem and its navigation item’s leftBarButtonItem to addButton.
Figure 10-28 The table view in edit mode
When the application starts, it presents the user with the table view. Upon tapping the Edit button, the application presents the table in edit mode (Figure 10-28). If the user clicks one of the table’s delete accessories, the accessory rotates 90 degrees and presents a Delete button (Figure 10-29). Upon clicking Delete, the row is deleted. When a user clicks and drags the move accessory on the right, he or she can move the row to the new location desired (Figure 10-30). If a user decides to add a row, he or she taps the Add button, which presents the user with a view to enter the new object’s details (Figure 10-31). If the user decides to cancel this action, he or she simply taps the Back button. If the user decides to save the record, he or she taps the Done button, which saves the record and returns the user to the table with the newly added table row (Figure 10-32). To support moving rows, you implemented the tableView:moveRowAtIndexPath: toIndexPath: method. In that method, you obtained the object from its old position in the tableDataList, removed it, and then added it to its new location. You also implemented the tableView:canMoveRowAtIndexPath: and tableView:canEditRowAtIndexPath: methods. If you needed to only allow certain rows to be editable, you would add that code to the tableView:canEditRowAtIndexPath: method. If you needed to only allow certain rows to be movable, you would add code to the tableView:canMoveRowAtIndexPath: method. Here, both methods simply return YES.
Figure 10-29 Deleting a row Figure 10-30 Moving a row
Figure 10-31 Adding an item Figure 10-32 The table view with thenewly added item
Supporting delete functionality requires a little more code than moving rows, but not much. You implemented the tableView:commitEditingStyle:forRowAtIndexPath: method. In the example, this method removes an object from the index and deletes the row from the table. Notice you disallowed inserting rows by implementing the tableView:editingStyleForRow AtIndexPath: and having it return UITableViewCellEditingStyleDelete. This method informs a table what accessory, if any, it should display in a row when the table enters edit mode. Here, it displays the delete accessory.You did not implement insert functionality at all. Inserting uses similar logic to deleting a row, only the style is UITableViewCellEditingStyleInsert. You would also implement code to initialize an object and then insert it in the data source at the proper location. For instance, in the previous example, you could change it to support inserting rows if you changed tableView: editingStyleForRowAtIndexPath: to return UITableViewCellEditingStyleInsert and added the following lines to the tableView:commitEditingStyle:forRowAtIndex: method: else if (editingStyle == UITableViewCellEditingStyleInsert) { self.tableDataList insertObject:@"Uninitialized" atIndex:indexPath. row]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES]; } In this example, however, adding a row does not use a table’s edit mode. Although it is possible to insert a row using a table’s edit mode, it is often not practical. Usually, a table lists items, where each row represents an individual item. Because each item has details associated with it, adding is often accomplished using a separate view controller. Here, you added a left bar button with an addition symbol. When a user clicks the button, it calls MyTableViewController’s enterAddMode method. This method presents the AddItemViewController’s view. Note that AddItemViewController and MyTableViewController both have references to each other. Upon finishing entering the new item, a user clicks Done, and the Done button fires AddItemViewController’s exitAndSave method. This method calls MyTableViewController’s exitAndSave method. and the row is added as the table’s last row. Listing 10-31 MyTableViewController.h #import <UIKit/UIKit.h> #import "TableViewDetailsViewController.h" #import "AddItemViewController.h" @interface MyTableViewController : UITableViewController { NSMutableArray * tableDataList; IBOutlet UINavigationController * navCont; AddItemViewController * addItemController; IBOutlet UIBarButtonItem * addButton; } @property (nonatomic, retain) UINavigationController * navCont; @property (nonatomic, retain) NSMutableArray * tableDataList; @property (nonatomic, retain) AddItemViewController * addItemController; @property (nonatomic, retain) UIBarButtonItem * addButton; (IBAction) exitAndSave: (NSString *) newValue; (IBAction) enterAddMode: (id) sender; @end Listing 10-32 MyTableViewController.m #import "MyTableViewController.h" @implementation MyTableViewController @synthesize tableDataList; @synthesize navCont; @synthesize addItemController; @synthesize addButton; - (IBAction) enterAddMode: (id) sender { self.addItemController = [[[AddItemViewController alloc] initWithNibName: @"AddItemViewController" bundle:nil] autorelease]; [self.navCont pushViewController:self.addItemController animated:YES]; self.addItemController.parentTable = self;} (void) exitAndSave : (NSString *) newValue { [self.tableDataList addObject: newValue]; [self.navCont popToRootViewControllerAnimated:YES]; [self.tableView reloadData]; }(void) viewDidLoad { NSMutableArray * tempArray = [[[NSMutableArray alloc] initWithObjects: @"Item One", @"Item Two", @"Item Three", @"Item Four", @"Item Five", @"Item Six", @"Item Seven", @"Item Eight", @"Item Nine", @"Item Ten", @"Item Eleven", @"Item Twelve", @"Item Thirteen", @"Item Fourteen", @"Item Fifteen", @"Item Sixteen", @"Item Seventeen", @"Item Eighteen", @"Item Nineteen", @"Item Twenty", nil] autorelease]; self.tableDataList = tempArray; self.navigationItem.rightBarButtonItem = self.editButtonItem; self.navigationItem.leftBarButtonItem = self.addButton; }(NSInteger) tableView : (UITableView *) tableView numberOfRowsInSection: (NSInteger) section { return [self.tableDataList count]; }(UITableViewCell *) tableView : (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: @"acell"]; if(cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:@"acell"] autorelease]; } cell.textLabel.text = [self.tableDataList objectAtIndex:[indexPath row]]; return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath { TableViewDetailsViewController * temp = [[ [TableViewDetailsViewController alloc] initWithNibName: @"TableViewDetailsViewController" bundle:nil] autorelease]; [self.navCont pushViewController:temp animated:YES]; } - (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath: (NSIndexPath *) indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { [self.tableDataList removeObjectAtIndex:indexPath.row]; [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES]; } else if (editingStyle == UITableViewCellEditingStyleInsert) { [self.tableDataList insertObject:@"Uninitialized" atIndex:indexPath. row]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES]; } } - (void)tableView:(UITableView *)tableView moveRowAtIndexPath: (NSIndexPath *) fromIndexPath toIndexPath:(NSIndexPath *) toIndexPath { id object = [[self.tableDataList objectAtIndex:fromIndexPath.row] retain]; [self.tableDataList removeObjectAtIndex:fromIndexPath.row]; [self.tableDataList insertObject:object atIndex: toIndexPath.row]; [object release]; } -(UITableViewCellEditingStyle) tableView: (UITableView *) tableView editingStyleForRowAtIndexPath: (NSIndexPath *) indexPath { return UITableViewCellEditingStyleDelete; }(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath: (NSIndexPath *) indexPath { return YES; }(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath { return YES; }(void)dealloc { [tableDataList release]; [navCont release]; [addItemController release]; [addButton release]; [super dealloc]; } @end Listing 10-33 AddItemViewController.h #import <UIKit/UIKit.h> @class MyTableViewController; @interface AddItemViewController : UIViewController { MyTableViewController * parentTable; IBOutlet UITextField * addedName; IBOutlet UIBarButtonItem * doneButton; } @property (nonatomic, retain) UITextField * addedName; @property (nonatomic, retain) MyTableViewController * parentTable; @property (nonatomic, retain) IBOutlet UIBarButtonItem * doneButton; - (IBAction) exitAndSave: (id) sender; @end Listing 10-34 AddItemViewController.m #import "AddItemViewController.h" @implementation AddItemViewController@synthesize addedName; @synthesize parentTable; @synthesize doneButton; @synthesize addedName; (void) viewDidLoad { navigationItem.title = @"Add Item"; self.navigationItem.rightBarButtonItem = self.doneButton; } (void)dealloc { [parentTable release]; [doneButton release]; [addedName release]; [super dealloc]; }(IBAction) exitAndSave: (id) sender { [self.parentTable exitAndSave:self.addedName.text]; } @end
You liked the article?
Like : 0
Vote for difficulty
Current difficulty (Avg): Medium
1/15
TekSlate is the best online training provider in delivering world-class IT skills to individuals and corporates from all parts of the globe. We are proven experts in accumulating every need of an IT skills upgrade aspirant and have delivered excellent services. We aim to bring you all the essentials to learn and master new technologies in the market with our articles, blogs, and videos. Build your career success with us, enhancing most in-demand skills in the market.
Stay Updated
Get stories of change makers and innovators from the startup ecosystem in your inbox