The UITableViewCell class can be configured to display in a variety of standard ways, but sometimes we want to display content in a cell that cannot be fit into one of the standard display modes. In this blog, we’ll use a table view to display a series of customized cells. So, let’s get started!

Start Xcode, select “Create a new Xcode project,” choose the Empty Application template, and click Next. Name the project CustomCells, and select options as shown:

Click Next, choose a location to save the project, then click Create.

We’ll be adding two new files to the project, a UITableViewController, and a xib file for our custom cell.

Choose File | New > File… from the menu, and add a UITableViewController named TableViewController as shown:

We do not need a xib file for this controller. Click Next and save the file in the default location by clicking Create.

Again choose File | New > File… from the menu, and this time create an empty xib file by selecting Empty in the User Interface section:

Click Next, make sure the Device Family is set to iPad, click Next, and save the file as CellNib in the default location by clicking Create.

Open the CellNib.xib file you just created. Drag a UITableViewCell from the library to the empty surface of the xib file, then add labels to the cell as shown here:

Set the text and for the labels as shown. In this example, the font size of the first label is 27, and it is set to System Italic. All other labels have the default font and size.

The next step is to set a tag value for each label that still has the text “Label.” Set the tag value for the large italic label to 1, then set tag values of 2, 3, 4, and 5 for the remaining “Label” labels to the right of Address 1:, Address 2:, Phone:, and Cell:.

In order for the table view controller to find this xib and use it as a template for each of its cells, we’ll need to change File’s Owner’s class in the xib. Select File’s Owner, then in the identity inspector, change the Class to “TableViewController” as shown here:

In the properties of the actual cell, set the identifier to Cell. We’ll be returning to this xib file in just a moment to wire up the cell to a property in the TableViewController, but first we have to create that property.

Open TableViewController.h and make these changes to the file:

#import <UIKit/UIKit.h>

@interface TableViewController : UITableViewController

@property (nonatomic, strong) NSArray *cellContent;
@property (nonatomic, strong) IBOutlet UITableViewCell *customCell;

@end

In this demo, we define an array to hold the content of the cells. We also define a UITableViewCell as an outlet called customCell. Save the file, then return to CustomCell.xib. Wire the customCell outlet to the cell as shown:

Now open TableViewController.m and make these changes:

#import "TableViewController.h"

@interface TableViewController ()

@end

@implementation TableViewController

@synthesize cellContent, customCell;

- (NSArray *)cellContent
{
    cellContent = [[NSArray alloc] initWithObjects:
                   [NSArray arrayWithObjects:@"Alex Ander",
                    @"213 4th St.", @"Apt. 17", @"555-555-5555", @"111-111-1111", nil],
                   [NSArray arrayWithObjects:@"Jane Doe",
                    @"4 Any Ave.", @"Suite 2", @"123-456-7890", @"098-765-4321", nil],
                   [NSArray arrayWithObjects:@"Bill Smith",
                    @"63 Smith Dr.", @"", @"678-765-1236", @"987-234-4987", nil],
                   [NSArray arrayWithObjects:@"Mike Taylor",
                    @"3145 Happy Ct.", @"", @"654-321-9871", @"654-385-1594", nil],
                   [NSArray arrayWithObjects:@"Nancy Young",
                    @"98 W. 98th St.", @"Apt. 3", @"951-753-9871", @"951-654-3557", nil],
                   nil];
    return cellContent;              
}

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;
 
    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
}

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

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
        return YES;
}

#pragma mark – Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return [[self.cellContent objectAtIndex:0] count];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 149.0f;
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:.75 alpha:1];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
   
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"CellNib" owner:self options:nil];
        cell = self.customCell;
        self.customCell = nil;
    }
   
    // Configure the cell…
    UILabel *textTarget;
    textTarget = (UILabel *)[cell viewWithTag:1]; //name
    textTarget.text = [[self.cellContent objectAtIndex:indexPath.row] objectAtIndex:0];
    textTarget = (UILabel *)[cell viewWithTag:2]; //addr1
    textTarget.text = [[self.cellContent objectAtIndex:indexPath.row] objectAtIndex:1];
    textTarget = (UILabel *)[cell viewWithTag:3]; //addr2
    textTarget.text = [[self.cellContent objectAtIndex:indexPath.row] objectAtIndex:2];
    textTarget = (UILabel *)[cell viewWithTag:4]; //phone
    textTarget.text = [[self.cellContent objectAtIndex:indexPath.row] objectAtIndex:3];
    textTarget = (UILabel *)[cell viewWithTag:5]; //cellPhone
    textTarget.text = [[self.cellContent objectAtIndex:indexPath.row] objectAtIndex:4];
   
    return cell;
}

#pragma mark – Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Navigation logic may go here. Create and push another view controller.
    /*
     <#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
     // …
     // Pass the selected object to the new view controller.
     [self.navigationController pushViewController:detailViewController animated:YES];
     */

}

@end

After synthesizing the properties, we override the getter of cellContent to lazily instantiate it to an array of arrays of strings holding names, addresses, and phone numbers. Since there is only one section in this table view, the numberOfSectionsInTableView: method returns 1. The numberOfRowsInSection: method just returns the count of inner arrays in the cellContent array.

In heightForRowAtIndexPath: we must return the height of the cell in CustomCell.xib. If you are entering this code from scratch, your cell height will most likely be a different value.

In order to change the background color of a cell, we must define the delegate method tableView: willDisplayCell: forRowAtIndexPath: method. This is the only opportunity we have to alter the background color of a UITableViewCell’s underlying view.

The tableView: cellForRowAtIndexPath: method first attempts to get a reusable cell. If there is no cell (cell == nil), then we load the nib file from the bundle (which populates the customCell property), set the cell to the customCell, then set the value of customCell to nil (so it doesn’t remain on the heap). After this, we configure the labels on the cell using their tags. Each label’s text is set to a value in the cellContent array. At the end of the method, we return the cell.

TableView: didSelectRowAtIndexPath: is shown here, but not implemented. We would most likely push a new view controller in response to the selection.

Only one thing remains: we must set up the views in AppDelegate in order for them to be displayed in the window. Open AppDelegate.h and make these changes:

#import <UIKit/UIKit.h>
#import "TableViewController.h"

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) TableViewController *tableViewController;
@property (strong, nonatomic) UINavigationController *navController;

@end

Make the following changes to the application: didFinishLaunchingWithOptions: method in AppDelegate.m. Remember to synthesize tableViewController and navController in AppDelegate.m as well!

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.tableViewController = [[TableViewController alloc] initWithStyle:UITableViewStylePlain];
    self.tableViewController.title = @"Table View";
    self.navController = [[UINavigationController alloc]
                          initWithRootViewController:self.tableViewController];
    [self.window addSubview:self.navController.view];
    [self.window makeKeyAndVisible];
    return YES;
}

Now run the application, and note that the default cells in the tableViewController have been replaced by our custom cells:

Table View Cells created in this way can contain any control; they are not limited to containing labels and images only. Since File’s Owner is the TableViewController, any action methods must be declared and defined in the view controller class, and will need to detect which cell contains the control being interacted with. But that is the subject of another blog…