Well, this one cost me an afternoon, but I think I figured it out. As far as I can tell, this appears to be a bug in how UITableViewCell is laying out the textLabel and detailTextLabel. When you set the row height, it seems to allocate equal height to the two labels, which means that you get exactly the behavior you're seeing above, even though detailTextLabel needs more room. Here are the two things I did to fix the problem. I had to subclass UITableViewCell to fix it, but it's a minimal amount of code.
First, make sure you're calculating the height of each row properly. Put this method into your table view delegate. Replace the font methods with your own:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellDetailText = [[self itemForIndexPath: indexPath] detailDescription];
NSString *cellText = [[self itemForIndexPath: indexPath] description];
// The width subtracted from the tableView frame depends on:
// 40.0 for detail accessory
// Width of icon image
// Editing width
// I don't think you can count on the cell being properly laid out here, so you've
// got to hard code it based on the state of the table.
CGSize constraintSize = CGSizeMake(tableView.frame.size.width - 40.0 - 50.0, CGFLOAT_MAX);
CGSize labelSize = [cellText sizeWithFont: [self cellTextLabelFont] constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
CGSize detailSize = [cellDetailText sizeWithFont: [self cellDetailTextLabelFont] constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
CGFloat result = MAX(44.0, labelSize.height + detailSize.height + 12.0);
return result;
}
Then, subclass UITableViewCell and override layoutSubviews:
#import "UITableViewCellFixed.h"
@implementation UITableViewCellFixed
- (void) layoutSubviews {
[super layoutSubviews];
self.textLabel.frame = CGRectMake(self.textLabel.frame.origin.x,
4.0,
self.textLabel.frame.size.width,
self.textLabel.frame.size.height);
self.detailTextLabel.frame = CGRectMake(self.detailTextLabel.frame.origin.x,
8.0 + self.textLabel.frame.size.height,
self.detailTextLabel.frame.size.width,
self.detailTextLabel.frame.size.height);
}
@end
Actually it CAN be resized if you create an UITableViewCell subclass and override the layoutSubviews method:
- (void)layoutSubviews {
[super layoutSubviews]; //The default implementation of the layoutSubviews
CGRect textLabelFrame = self.textLabel.frame;
textLabelFrame.size.width = _textLabelMaxWidth;
self.textLabel.frame = textLabelFrame;
}
Hope it helps.
Best Answer
The docs describe -[UIView layoutSubviews] as
That description is not elaborate, but is accurate. In this case, the method's behavior is to layout your subviews. It will be invoked anytime the device orientation changes. It is also scheduled for later invocation whenever you call -setNeedsLayout.
Since, UIView's implementation does nothing, (and I presume the same for UIControl), you get total creative freedom to make your UIView subclass subviews be positioned wherever you want them.
In subclassing a UITableViewCell, you have a couple of options to try:
In a related SO question, the recommendation is to avoid #1, manipulating the built-in textLabel and detailTextLabel.
A more reliable bet would be to do something like this:
In MyTableViewCell, you would ignore the built-in text labels and use your own custom UILabels. You take complete control over positioning them within the content rect of the table view cell.
I'm leaving a lot of stuff out. In doing custom layout with text labels, you'd want to consider:
Figuring out your own layout algorithm.
I'm using a layout algorithm above that resizes the custom UILabels to fit their text content, then positions them side-by-side. You'll likely want something more specific to your app.
Keeping the custom labels within the content view.
In -layoutSubviews, you might want logic to keep the custom UILabels sized and positioned so that they don't fall outside the bounds of the content rect. With my naive layout logic, any long text dropped into either UILabel (or both) could cause the labels to be positioned right out of the content view bounds.
How to handle -viewDidLoad/-viewDidUnload.
As coded above, this subclass doesn't handle being loaded from a nib. You might want to use IB to layout your cell, and if you do, you'll need to think about -viewDidLoad/-viewDidUnload/-initWithCoder: