Reputation: 3238
I have a grouped UITableView that is not rendering as expected. I am programmatically adding between 2 and 5 UILabels to each UITableViewCell using [cell.contentView addSubview:...]
and am using tableView:heightForRowAtIndexPath
to specify the row height based on the number of labels I add. I've read numerous related posts on stackoverflow and have read A Closer Look at Table-View Cells but a solution isn't clicking for me. My only thought at this point is to create a nib for every cell variation (row #1 with 3 lines, row #1 with 4 lines, row #1 with 5 lines) etc... but that seems a bit drastic.
Screenshot of problem:
I created a test project with one table view controller extending UITableViewController. IBOutlet is set. Here are content of .m:
- (id)initWithStyle:(UITableViewStyle)style
{
return [super initWithStyle:UITableViewStyleGrouped];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1; // Hardcoding for example
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 4; // Hardcoding for example
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = @"Detail Cell";
// Table view will have between 1 and 4 rows and all cells are different... don't think dequeue is needed in this scenario
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
NSArray *labels = [self makeLabelsForRows];
// Add labels for this row
for (UILabel *label in [labels objectAtIndex:indexPath.row]) {
[cell.contentView addSubview:label];
}
// changing the cell.frame here doesn't seem to help.
// Removed per Ricard Pérez del Campo's comment
// CGFloat theHeight = [self tableView:self.tableView heightForRowAtIndexPath:indexPath];
// cell.frame = CGRectMake(0, 0, 320, theHeight);
NSLog(@"Cell = %@", cell);
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Normally these are calculated based on cell content. Hardcoding for example
if (indexPath.row == 0) return 123.0;
else if (indexPath.row == 1) return 73.0;
else if (indexPath.row == 2) return 62.0;
else if (indexPath.row == 3) return 62.0;
return 44.0;
}
- (NSArray *)makeLabelsForRows
{
// Array of arrays containing UILables
NSArray *retVal = [NSArray array];
// Stores the height of the row
double y = 0.0f;
// Showing all rows and labels for example
// ROW #1
NSArray *row1 = [NSArray array];
UILabel *eventTitleLabel, *calendarTitleLabel, *locationLabel, *dateLabel, *timeLabel;
eventTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(30.0, y, 280.0, 21.0)];
eventTitleLabel.tag = 2;
eventTitleLabel.font = [UIFont boldSystemFontOfSize:17.0];
eventTitleLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
eventTitleLabel.text = @"Event Title";
eventTitleLabel.backgroundColor = [UIColor clearColor];
row1 = [row1 arrayByAddingObject:eventTitleLabel];
y += 21.0f;
locationLabel = [[UILabel alloc] initWithFrame:CGRectMake(30.0, y, 280.0, 21.0)];
locationLabel.tag = 3;
locationLabel.font = [UIFont systemFontOfSize:15.0];
locationLabel.textColor = [UIColor darkGrayColor];
locationLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
locationLabel.backgroundColor = [UIColor clearColor];
locationLabel.text = @"Event Location";
row1 = [row1 arrayByAddingObject:locationLabel];
y += 19.0f;
calendarTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(30.0, y, 280.0, 21.0)];
calendarTitleLabel.tag = 4;
calendarTitleLabel.font = [UIFont systemFontOfSize:15.0];
calendarTitleLabel.textColor = [UIColor redColor];
calendarTitleLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
calendarTitleLabel.backgroundColor = [UIColor clearColor];
calendarTitleLabel.text = @"Calendar Title";
row1 = [row1 arrayByAddingObject:calendarTitleLabel];
y += 19.0f;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterFullStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
dateLabel = [[UILabel alloc] initWithFrame:CGRectMake(30.0, y, 280.0, 21.0)];
dateLabel.tag = 5;
dateLabel.font = [UIFont boldSystemFontOfSize:12.0];
dateLabel.textColor = [UIColor blueColor];
dateLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
dateLabel.backgroundColor = [UIColor clearColor];
dateLabel.text = [dateFormatter stringFromDate:[NSDate date]];
row1 = [row1 arrayByAddingObject:dateLabel];
y += 16.0f;
[dateFormatter setDateStyle:NSDateFormatterNoStyle];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(30.0, y, 280.0, 21.0)];
timeLabel.tag = 6;
timeLabel.font = [UIFont boldSystemFontOfSize:12.0];
timeLabel.textColor = [UIColor blueColor];
timeLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
timeLabel.backgroundColor = [UIColor clearColor];
timeLabel.text = [dateFormatter stringFromDate:[NSDate date]];
row1 = [row1 arrayByAddingObject:timeLabel];
y += 16.0f;
retVal = [retVal arrayByAddingObject:row1];
// ROW #2
y = 0.0f;
NSArray *row2 = [NSArray array];
UILabel *rowTitleLabel, *alert1Label, *alert2Label;
rowTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(30.0, y, 280.0, 21.0)];
rowTitleLabel.tag = 2;
rowTitleLabel.font = [UIFont boldSystemFontOfSize:15.0];
rowTitleLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
rowTitleLabel.text = @"Alert";
rowTitleLabel.backgroundColor = [UIColor clearColor];
row2 = [row2 arrayByAddingObject:rowTitleLabel];
y += 21.0f;
alert1Label = [[UILabel alloc] initWithFrame:CGRectMake(30.0, y, 280.0, 21.0)];
alert1Label.tag = 6;
alert1Label.font = [UIFont boldSystemFontOfSize:12.0];
alert1Label.textColor = [UIColor blueColor];
alert1Label.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
alert1Label.backgroundColor = [UIColor clearColor];
alert1Label.text = @"Alert #1 5 min";
row2 = [row2 arrayByAddingObject:alert1Label];
y += 16.0f;
alert2Label = [[UILabel alloc] initWithFrame:CGRectMake(30.0, y, 280.0, 21.0)];
alert2Label.tag = 6;
alert2Label.font = [UIFont boldSystemFontOfSize:12.0];
alert2Label.textColor = [UIColor blueColor];
alert2Label.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
alert2Label.backgroundColor = [UIColor clearColor];
alert2Label.text = @"Alert #2 15 min";
row2 = [row2 arrayByAddingObject:alert2Label];
y += 16.0f;
retVal = [retVal arrayByAddingObject:row2];
// ROW #3
y = 0.0f;
NSArray *row3 = [NSArray array];
UILabel *rowTitleLabel2, *urlLabel;
rowTitleLabel2 = [[UILabel alloc] initWithFrame:CGRectMake(30.0, y, 280.0, 21.0)];
rowTitleLabel2.tag = 2;
rowTitleLabel2.font = [UIFont boldSystemFontOfSize:15.0];
rowTitleLabel2.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
rowTitleLabel2.text = @"URL";
rowTitleLabel2.backgroundColor = [UIColor clearColor];
row3 = [row3 arrayByAddingObject:rowTitleLabel2];
y += 21.0f;
urlLabel = [[UILabel alloc] initWithFrame:CGRectMake(30.0, y, 280.0, 21.0)];
urlLabel.tag = 6;
urlLabel.font = [UIFont boldSystemFontOfSize:12.0];
urlLabel.textColor = [UIColor blueColor];
urlLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
urlLabel.backgroundColor = [UIColor clearColor];
urlLabel.text = @"www.here.com";
row3 = [row3 arrayByAddingObject:urlLabel];
y += 21.0f;
retVal = [retVal arrayByAddingObject:row3];
// ROW #4
y = 0.0f;
NSArray *row4 = [NSArray array];
UILabel *rowTitleLabel3, *notesLabel;
rowTitleLabel3 = [[UILabel alloc] initWithFrame:CGRectMake(30.0, y, 280.0, 21.0)];
rowTitleLabel3.tag = 2;
rowTitleLabel3.font = [UIFont boldSystemFontOfSize:15.0];
rowTitleLabel3.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
rowTitleLabel3.text = @"Notes";
rowTitleLabel3.backgroundColor = [UIColor clearColor];
row4 = [row4 arrayByAddingObject:rowTitleLabel3];
y += 21.0f;
notesLabel = [[UILabel alloc] initWithFrame:CGRectMake(30.0, y, 280.0, 21.0)];
notesLabel.tag = 6;
notesLabel.font = [UIFont boldSystemFontOfSize:12.0];
notesLabel.textColor = [UIColor blueColor];
notesLabel.backgroundColor = [UIColor clearColor];
notesLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
notesLabel.text = @"Boom, notes!";
notesLabel.lineBreakMode = UILineBreakModeWordWrap;
row4 = [row4 arrayByAddingObject:notesLabel];
y += 21.0f;
retVal = [retVal arrayByAddingObject:row4];
return retVal;
}
Dump of all subviews per request:
2012-07-26 14:04:14.333 TestLabels[72259:f803] cell.contentView subviews for row #0 = (
"<UILabel: 0x6da3590; frame = (30 0; 280 21); text = 'Event Title'; clipsToBounds = YES; autoresize = LM+H; userInteractionEnabled = NO; tag = 2; layer = <CALayer: 0x6da3630>>",
"<UILabel: 0x6da3c90; frame = (30 21; 280 21); text = 'Event Location'; clipsToBounds = YES; autoresize = LM+H; userInteractionEnabled = NO; tag = 3; layer = <CALayer: 0x6da3d80>>",
"<UILabel: 0x6da3e00; frame = (30 40; 280 21); text = 'Calendar Title'; clipsToBounds = YES; autoresize = LM+H; userInteractionEnabled = NO; tag = 4; layer = <CALayer: 0x6da3dd0>>",
"<UILabel: 0x6885b20; frame = (30 59; 280 21); text = 'Thursday, July 26, 2012'; clipsToBounds = YES; autoresize = LM+H; userInteractionEnabled = NO; tag = 5; layer = <CALayer: 0x6883950>>",
"<UILabel: 0x6883a50; frame = (30 75; 280 21); text = '2:04 PM'; clipsToBounds = YES; autoresize = LM+H; userInteractionEnabled = NO; tag = 6; layer = <CALayer: 0x6885790>>"
)
2012-07-26 14:04:14.337 TestLabels[72259:f803] cell.contentView subviews for row #1 = (
"<UILabel: 0x6da8730; frame = (30 0; 280 21); text = 'Alert'; clipsToBounds = YES; autoresize = LM+H; userInteractionEnabled = NO; tag = 2; layer = <CALayer: 0x6da6ad0>>",
"<UILabel: 0x6da6c10; frame = (30 21; 280 21); text = 'Alert #1 5 min'; clipsToBounds = YES; autoresize = LM+H; userInteractionEnabled = NO; tag = 6; layer = <CALayer: 0x6da70b0>>",
"<UILabel: 0x6da7220; frame = (30 37; 280 21); text = 'Alert #2 15 min'; clipsToBounds = YES; autoresize = LM+H; userInteractionEnabled = NO; tag = 6; layer = <CALayer: 0x6da70e0>>"
)
2012-07-26 14:04:14.340 TestLabels[72259:f803] cell.contentView subviews for row #2 = (
"<UILabel: 0xbe71950; frame = (30 0; 280 21); text = 'URL'; clipsToBounds = YES; autoresize = LM+H; userInteractionEnabled = NO; tag = 2; layer = <CALayer: 0xbe71a90>>",
"<UILabel: 0xbe719c0; frame = (30 21; 280 21); text = 'www.here.com'; clipsToBounds = YES; autoresize = LM+H; userInteractionEnabled = NO; tag = 6; layer = <CALayer: 0xbe71d00>>"
)
2012-07-26 14:04:14.342 TestLabels[72259:f803] cell.contentView subviews for row #3 = (
"<UILabel: 0x6a71750; frame = (30 0; 280 21); text = 'Notes'; clipsToBounds = YES; autoresize = LM+H; userInteractionEnabled = NO; tag = 2; layer = <CALayer: 0x6a718e0>>",
"<UILabel: 0x6a717c0; frame = (30 21; 280 21); text = 'Boom, notes!'; clipsToBounds = YES; autoresize = LM+H; userInteractionEnabled = NO; tag = 6; layer = <CALayer: 0x6a716e0>>"
)
Upvotes: 1
Views: 1184
Reputation: 5812
Based on the height logs you have posted, I think if you reduce the height for row 1, (instead of 123.0, try 96.0 or 100.0), and also make sure that the labels have the autoresizing masks set right.
I think your labels have an autoresizingMask with flexibletopMargin. And thats making your labels to stick to the bottom of the cell.contentView. Let me know if this helps.
Upvotes: 1
Reputation: 14886
There's a method the UITableView calls (if it's in your class) where you need to specify the height of each row.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat cellHeight = 44; // default height
for (int i = 0; i < [labels count]; i++) {
cellHeight += 44; // or whatever the size of each of the labels you need
}
return cellHeight;
}
This is just a basic loop where you add 44 pixels to the cell's height for each label. If you get into more custom drawing, etc. you may be required to do a lot more calculations to figure out the height the cell needs to be drawn with.
Also, this method is a bit expensive. The above code it's really that expensive, but if you start having to calculate the height based on text that wraps, layout of graphics, etc. it does damage the scrolling frame rate a little bit on older devices.
Upvotes: 0
Reputation: 2417
With table views, you don't have to manage the cell's frame manually. It is the table view delegate the responsible to set its height. So in your
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
you should not set the cells frame.
Upvotes: 0