Example of using a UITableView to create a form Example of using a UITableView to create a form

Posted by jstrecker on 2011.03.31 @ 23:38

Filed under:

There are a lot of iPhone/iPad examples out there, but surprisingly I haven’t seen one for a common task – using a table view to create a form. Here’s the source code.

In this example, we’ll create a form with 2 fields and a button.

When the user clicks Done, an alert appears.

Nice things about this example:

  • UI element placement and sizing (in the xib) is separated from logic (in the view controller class).
  • All UI elements in the form are contained in one xib.
  • Table view cells can be conditionally included or excluded from the table in -viewDidLoad. The table easily accommodates a mixture of xib-defined cells and cells created at runtime.
  • Table view cells can have different sizes.

Setup

In Xcode:

  1. Create a subclass of UIViewController called FormController.
  2. Create a View xib called Form.xib.

Build the UI

In Form.xib:

  1. Set File’s Owner to FormController.
  2. Drop a Table View inside the View.
  3. Drop Table View Cells into the xib, but not inside the View.
  4. Add the controls you want to each Table View Cell. If needed, increase the height of the Table View Cell to fit.
  5. Somewhere, add a ‘Done’ button to process the form. (I added mine as a Bar Button Item in a Toolbar.)

Form.xib should now look something like this:

Write the code

In FormController.h and FormController.m:

1. Add an IBOutlet for each UITableViewCell.

@property (nonatomic, retain) IBOutlet UITableViewCell *phaseCell; 
@property (nonatomic, retain) IBOutlet UITableViewCell *heisenCell; 

2. Add an IBOutlet for each control to be accessed in the code.

@property (nonatomic, retain) IBOutlet UISlider *phaseSlider; 
@property (nonatomic, retain) IBOutlet UISwitch *heisenSwitch; 

3. In -viewDidLoad, add each UITableViewCell to an array called cells. Make this array a property so we can access it from other methods – the UITableViewDataSource and UITableViewDelegate methods will refer to it. (If the set of cells in the table view should vary, here is where you would decide whether to add each cell to the array, or construct additional UITableViewCell objects to add to the array.)

self.cells = [NSArray arrayWithObjects:phaseCell, heisenCell, nil]; 

4. After that line in -viewDidLoad, make sure each UITableViewCell does not change its appearance when selected. (Technically this is a UI property and you might argue that it belongs in the xib, but I prefer to put it here to make sure no cells are forgotten.)

for (UITableViewCell *cell in cells)
    cell.selectionStyle = UITableViewCellSelectionStyleNone; 

5. Make FormController a UITableViewDataSource and implement the protocol methods by referring to the cells array.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{
    return 1; 
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{
    return [cells count]; 
}
 
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{    
    return [cells objectAtIndex:indexPath.row]; 
}

6. If any of the table view cells are taller than the default height, make FormController a UITableViewDelegate and implement heightForRowAtIndexPath by referring to the cells array.

- (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [cells objectAtIndex:indexPath.row]; 
    return cell.bounds.size.height;  // use the height set in IB
}

7. Add an IBAction method to process the form in response to the ‘Done’ button.

- (IBAction)done:(id)sender
{
    NSString *phaseMsg = [NSString stringWithFormat:@"Adjust phase variance to %f", phaseSlider.value]; 
    NSString *heisenMsg = (heisenSwitch.on ? @"compensate" : @"create a diversion"); 
    NSString *msg = [NSString stringWithFormat:@"%@, then %@.", phaseMsg, heisenMsg]; 
 
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Captain's Orders"
                                                    message:msg
                                                   delegate:nil 
                                          cancelButtonTitle:@"OK" 
                                          otherButtonTitles:nil]; 
    [alert show]; 
    [alert release]; 
}

Make the connections

In Form.xib:

  1. Connect up the IBOutlets and IBActions between File’s Owner and the Table View Cells and controls.

  2. Connect the Table View’s dataSource and delegate outlets to File’s Owner.

The connections for Form.xib should now look something like this:

Good intro. Thanks