[Cocoa + iPhone] UITableViewCell: It’s Broken!

I present for your consideration the following screenshot:

UITableViewCell is broken!

It shows a basic table-view, in which each cell has been assigned the same image (using its built-in ‘imageView‘ property). The source image is 20 pixels square, and the imageView’s ‘contentMode‘ property has not been changed (not that changing it makes any difference). The image for each row is also being scaled to 50% and rendered at the orientation stated in the cell text. The code for the table controller is as follows:

#import "UITableViewTestViewController.h"

static NSString* rowNames[8] = {@"UIImageOrientationUp", @"UIImageOrientationDown", @"UIImageOrientationLeft", @"UIImageOrientationRight", 
                                @"UIImageOrientationUpMirrored", @"UIImageOrientationDownMirrored", @"UIImageOrientationLeftMirrored", 
                                @"UIImageOrientationRightMirrored"};

#define IMAGE_NAME @"testImage.png"

@implementation UITableViewTestViewController

- (void)dealloc {
    [super dealloc];
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
}

#pragma mark - View lifecycle
- (int) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 8;  //number of elements in the enumeration
}

- (int) numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString* cellIdentifier = @"TestCell";
    
    //return a basic cell with the icon in it and some text
    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"StationCell"];
    if (cell == nil) {
        //init cell
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellIdentifier] autorelease];
    }
    
    cell.accessoryType = UITableViewCellAccessoryNone;
    cell.textLabel.text = rowNames[indexPath.row];          //enum starts from 0, so indexPath.row matches the orientation that we are going to apply
    cell.textLabel.font = [cell.textLabel.font fontWithSize:12.0];
    cell.textLabel.textColor = [UIColor darkGrayColor];
    cell.imageView.image =  [UIImage imageWithCGImage:[UIImage imageNamed:IMAGE_NAME].CGImage scale:0.5 orientation:indexPath.row];  //the scale operation will be ignored for UIImageOrientationUp; because something is broken
    
    return cell;
}

- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    //it makes no difference if we set the image here
    //cell.imageView.image =  [UIImage imageWithCGImage:[UIImage imageNamed:IMAGE_NAME].CGImage scale:0.5 orientation:indexPath.row];
}
@end

It’s not doing anything all that special, but as you can see in the screenshot the image in the first cell is rendered differently than all the others. More specifically, it is being stretched to the full size of its container so that it just looks kind of sad, and no amount of programmatic scale operations will fix it.

This can be one of the most maddening aspects about working with table-cells and images. If you want an image that is slightly smaller than its container in the table-cell, or that is centered away from the top/side, then the only consistent way to do so is to create a custom table-cell. And while it is not difficult to create a custom table-cell that implements the desired behavior, it needlessly clutters the source-tree with code that replicates functionality that Apple is supposed to be providing out of the box.

The problem, as exposed by this example code, is that when an image is scaled using UIImageOrientationUp (which is what most developers would use, given that they generally store their images in the orientation they want them displayed at) the UITableViewCell completely ignores the scaling operation. I can only speculate as to the reason for this odd behavior, because at the very least I would expect the output to be the same no matter what UIImageOrientation is used (i.e. I would think that scaling would either consistently not work or consistently work, but this is manifestly not the case).

In any case, this behavior is very clearly a bug, and a particularly inconvenient one at that. But it does expose a potential workaround that generates less source-clutter than creating a custom table-cell implementation every time you want to have cell images that actually work. Just store your images upside-down (or preprocess them so that they are upside-down prior to adding to the table) and then invert them back to the proper orientation when you scale them to the size you want for your table.

It’s dodgy as all hell to do it that way, but still arguably better than reimplementing functionality that Apple is supposed to be providing out of the box.

Project source code is available here: http://codethink.no-ip.org/UITableViewTest.zip

This entry was posted in coding, objective-c and tagged , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>