Christopher Oldfield
Christopher Oldfield

Reputation: 139

Subclassing UITableViewCell (and possibly using id)

I'm trying to find a way to not have code being duplicated when I set up custom cells in my table view. I currently have a BaseCell class, subclassed from UITableViewController and then two subclasses of BaseCell: CellTypeOne and CellTypeTwo. This is the sort of setup I'm using at the moment:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *type = [self.rowTypes objectAtIndex:indexPath.row];

    if ([type isEqualToString:@"Type 1"]) {
        CellTypeOne *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell 1"];

        cell.picture.image = // An image
        cell.caption.text = // A caption

        cell.author.text = // An author

        return cell;
    }

    else if {[type isEqualToString:@"Type 2"]) {
        CellTypeTwo *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell 2"];

        cell.picture.image = // An image
        cell.caption.text = // A caption

        cell.number.text = // A number

        return cell;
    }
}

As some of the properties being set are the same for either cell type, I'm wondering if there's a way to do something like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *type = [self.rowTypes objectAtIndex:indexPath.row];

    id cell;

    if ([type isEqualToString:@"Type 1"]) {
        cell = [tableView dequeueReusableCellWithIdentifier:@"Cell 1"];

        // Cast cell to (CellTypeOne *)?

        cell.author.text = // An author
    }

    else if {[type isEqualToString:@"Type 2"]) {
        CellTypeTwo *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell 2"];

        // Cast cell to (CellTypeTwo *)?

        cell.number.text = // A number
    }

    cell.picture.image = // An image
    cell.caption.text = // A caption

    return cell;
}

This way I can do class specific setup for each subclass and then do generic setup for both. The only way I've gotten this to work is by casting cell each time I need to use it, something like this:

[(BaseCell *)cell picture].image = // An image

Having to cast cell on each line seems like more work than having a couple of lines of duplicated code to begin with, but I'm just trying to figure out the best way to go about this.

Upvotes: 2

Views: 328

Answers (1)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726489

You can make the cell variable of type BaseCell* to avoid casts outside ifs:

BaseCell *cell;

if ([type isEqualToString:@"Type 1"]) {
    cell = [tableView dequeueReusableCellWithIdentifier:@"Cell 1"];
    [[cell author] setText:/*an author*/];
} else if {[type isEqualToString:@"Type 2"]) {
    cell = [tableView dequeueReusableCellWithIdentifier:@"Cell 2"];
    [[cell number] setText: /* a number */];
}

cell.picture.image = // An image
cell.caption.text = // A caption

Alternatively, you can keep the type of id, and use method invocation syntax rather than member (dot) syntax outside the ifs:

id cell;
// ... the code from your post
[[cell picture] setImage: /* an image */];
[[cell caption] setText: /* a caption */];

Finally, you can avoid casts and square brackets altogether by declaring additional variables inside your ifs:

BaseCell *cell;

if ([type isEqualToString:@"Type 1"]) {
    CallTypeOne *cellOne = [tableView dequeueReusableCellWithIdentifier:@"Cell 1"];
    cellOne.author.text = // An author
    cell = cellOne;
} else if {[type isEqualToString:@"Type 2"]) {
    CellTypeTwo *cellTwo = [tableView dequeueReusableCellWithIdentifier:@"Cell 2"];
    cellTwo.number.text = // A number
    cell = cellTwo;
}

Upvotes: 2

Related Questions