Josh Kahane
Josh Kahane

Reputation: 17169

Multiple UITableViewCell Classes in One UITableView?

I am putting together a TableView and I need to have multiple cell classes used in the same table.

So for example how would I use more than one cell class in my cellForRowAtIndexPath method?

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    // Configure the cell...

    return cell;
}

I know I could simply replace UITableViewCell with my custom class and use if statements to adjust the class depending on the index, but isn't that a bit messy, what would be a reasonable, and possibly the most intelligent way of doing this?

Upvotes: 7

Views: 6860

Answers (3)

Brian
Brian

Reputation: 7146

Sure can. Just create new instances of your custom cells and give them a new CellId.

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {

   if (condition1)
    {
        static NSString *CellIdentifier1 = @"Cell1";
        UITableViewCell *cell = 
         [tableView dequeueReusableCellWithIdentifier:CellIdentifier1];

        // TODO: if cell is null, create new cell of desired type.  
        // This is where you    create your custom cell that is 
        // derived form UITableViewCell.
    }
    else
    {
        static NSString *CellIdentifier2 = @"Cell2";
        UITableViewCell *cell = 
         [tableView dequeueReusableCellWithIdentifier:CellIdentifier2];

        //TODO: if cell is null, create new cell of desired type

    }
    // Configure the cell...

    return cell;
}

Upvotes: 9

Kaelin Colclasure
Kaelin Colclasure

Reputation: 3975

You can do this by defining the table to have several prototype cells in its .xib file or Storyboard. Note the setting "Dynamic Prototypes" for the table view in the Xcode screen shot below:

enter image description here

Each prototype cell must be given a unique reuse identifier. In this example, the first cell type has a reuse identifier @"ScanCell", and the second @"DetailCell". Then, in your -tableView:cellForRowAtIndexPath: method you simply choose which class of cell to use by choosing which reuse identifier you pass to -dequeueReusableCellWithIdentifier:.

Here is an example taken from one of my own apps:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString * cellIdentifier = @"DetailCell";
    if ([indexPath section] == 0) {
        cellIdentifier = @"ScanCell";
    }
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier
                                                             forIndexPath:indexPath];
    if ([indexPath section] == 1) {
        CBPeripheral * peripheral = (CBPeripheral *)[self.appDelegate.peripherals objectAtIndex:[indexPath row]];
        cell.textLabel.text = [peripheral name];
        cell.detailTextLabel.text = [self.appDelegate.peripheralKeys objectAtIndex:[indexPath row]];
    }
    return cell;
}

If you want the prototype cells to have different classes, simply set their class in the .xib or Storyboard file.

Upvotes: 8

nielsbot
nielsbot

Reputation: 16032

At some point you will need to associate different cell classes with the different item types in your data source. An if statement might be the way to go. You might encapsulate this in a separate method, like this:

+(Class)cellClassForItem:(id)rowItem
{
    Class theClass = [ UITableViewCell class ] ;

    if ( [ rowItem isKindOfClass:[ ... class ] ] )
    {
        theClass = [ CellClass1 class ] ;
    }
    else if ( [ rowItem isKindOfClass:[ ... class ] ] )
    {
        theClass = [ CellClass2 class ] ;
    }

    return theClass ;
}

Or, you might have each item in your data source implement a protocol:

@protocol DataSourceItem <NSObject>
-(Class)tableViewCellClass ;
@end

Now, your delegate method would look like this (assuming the second technique)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    id<DataSourceItem> item = (id<DataSourceItem>)[ tableView itemForRowAtIndexPath:indexPath ] ;
    Class cellClass = [ item tableViewCellClass ] ;
    NSString * cellID = NSStringFromClass( cellClass ) ;

    UITableViewCell *cell = [ tableView dequeueReusableCellWithIdentifier:cellID ] ;
    if ( !cell )
    {
        cell = [ [ cellClass alloc ] initWithStyle:... reuseIdentifier:cellID ] ;
    }

    cell.value = item ;

    return cell;
}

Upvotes: 3

Related Questions