151

This is on iPhone 0S 2.0. Answers for 2.1 are fine too, though I am unaware of any differences regarding tables.

It feels like it should be possible to get text to wrap without creating a custom cell, since a UITableViewCell contains a UILabel by default. I know I can make it work if I create a custom cell, but that's not what I'm trying to achieve - I want to understand why my current approach doesn't work.

I've figured out that the label is created on demand (since the cell supports text and image access, so it doesn't create the data view until necessary), so if I do something like this:

cell.text = @""; // create the label
UILabel* label = (UILabel*)[[cell.contentView subviews] objectAtIndex:0];

then I get a valid label, but setting numberOfLines on that (and lineBreakMode) doesn't work - I still get single line text. There is plenty of height in the UILabel for the text to display - I'm just returning a large value for the height in heightForRowAtIndexPath.

Vadim Kotov
  • 7,103
  • 8
  • 44
  • 57
Airsource Ltd
  • 31,336
  • 13
  • 69
  • 74

10 Answers10

278

Here is a simpler way, and it works for me:

Inside your cellForRowAtIndexPath: function. The first time you create your cell:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
    cell.textLabel.numberOfLines = 0;
    cell.textLabel.font = [UIFont fontWithName:@"Helvetica" size:17.0];
}

You'll notice that I set the number of lines for the label to 0. This lets it use as many lines as it needs.

The next part is to specify how large your UITableViewCell will be, so do that in your heightForRowAtIndexPath function:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellText = @"Go get some text for your cell.";
    UIFont *cellFont = [UIFont fontWithName:@"Helvetica" size:17.0];
    CGSize constraintSize = CGSizeMake(280.0f, MAXFLOAT);
    CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];

    return labelSize.height + 20;
}

I added 20 to my returned cell height because I like a little buffer around my text.

Lee Taylor
  • 6,091
  • 14
  • 26
  • 43
Tim Rupe
  • 4,321
  • 1
  • 19
  • 23
  • 17
    You do not need to set your cell.textLabel.numberOfLines to some arbitrarily high number. Setting it to 0 means "as many lines as it takes to display." – mmc Jun 07 '09 at 15:07
  • Thanks, I'll edit my post tonight after I get a chance to verify it in my own project. – Tim Rupe Jun 08 '09 at 15:58
  • 1
    Setting number of lines to 0 works fine (as many lines as it takes to display) – prakash Jun 17 '09 at 04:57
  • when I set the numberOfLines to 0, I always have a blank line at the end. Any ideas how I can get rid of that? – dlinsin Jan 17 '10 at 11:14
  • David, You probably have a newline at the end of your string. That's the only cause that comes to mind. – Tim Rupe Jan 18 '10 at 17:58
  • This works beautifully - thank you! Because some of my rows aren't "multiline" I selectively calculate the row height (and set numberOfLines and lineBreakMode as needed) ... else I just return tableView.rowHeight as a default. Tip for those trying this out: Be sure you do NOT reference your table view cell within tableView:heightForRowForIndexPath:. As handy as that would be (to get at the font for a given cell, for instance), this will cause an infinite loop, "and your debugger will be sad." – Joe D'Andrea May 11 '10 at 00:25
  • This works really well for me up to a point. I can only get three lines after that my text is truncated. Any idea what would cause this? I'm using the same code above with the exception of using the detailTextLabel instead of the textLabel – cagreen May 12 '10 at 18:26
  • @cagreen - This probably doesn't work for detailTextLabel because of subview layouts being overridden. If you really want to continue in the same direction, making a custom cell and manually managing the layoutSubviews is probably your best bet. – Tim Rupe May 12 '10 at 21:34
  • 1
    cagreen: Turns out I can replicate this, but *only* if I use iPhone OS 3.0 in the simulator. When I use 3.1+, the detailTextLabel's resizing matches that of sizeWithFont just fine. So Tim's method works really well - just for 3.1+ (likely due to a bug/misfeature in the 3.0 default cell rendering). For the record, I'm using a vertical top/bottom margin of 12 (each), a detail text label size of (188.0, CGFLOAT_MAX), and a boldSystemFontOfSize 15. – Joe D'Andrea May 12 '10 at 21:34
  • What would my cellFont be if my cells have a variety of fonts and sizes? - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *cellText = @"Go get some text for your cell."; UIFont *cellFont = [UIFont fontWithName:@"Helvetica" size:17.0]; } – Patricia Oct 27 '10 at 20:46
  • @Patricia: That gets a bit more complicated, but essentially you'll need to track what font and size each cell has in an array or table that is indexed by the cell number. When you enter this function, look up the information you need, and apply it to get the cell size you need. – Tim Rupe Oct 27 '10 at 22:54
  • @Patricia - Try getting hold of your cell and referencing the cell.textLabel.font instead of using an array of fonts used. Essentially, you can get your cell by calling the UITableViewCellDelegate method tableView:cellForRowAtIndexPath:. Hope that helps – Shiun May 23 '12 at 16:25
  • Any solution for rightDetail being clipped because the left label is too long? Can't come up with anything, being setting frame of left label or increasing height of cell. – Matej Aug 17 '12 at 00:54
  • A Great solution. Tried several other similar methods, but this works the best! :) – Johan Karlsson Dec 12 '12 at 07:37
  • 17
    UILineBreakModeWordWrap is deprecated in iOS 6. Use NSLineBreakByWordWrapping instead. – Ethan Allen Jan 19 '13 at 22:07
  • @EthanAllen - That's fine. But don't change the answer like that. Add an edit line or comment it in some other way. – Lee Taylor Jan 19 '13 at 22:18
  • can somebody translate this to swift. i couldnt get the CGSize labelSize = part right. – Esqarrouth Dec 21 '14 at 17:23
  • @Esq - This, more Swift oriented answer, works for me: http://stackoverflow.com/a/25187891/2371425 – Sakiboy Jun 28 '15 at 22:13
17

Updated Tim Rupe's answer for iOS7:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
    cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
    cell.textLabel.numberOfLines = 0;
    cell.textLabel.font = [UIFont fontWithName:@"Helvetica" size:17.0];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellText = @"Go get some text for your cell.";
    UIFont *cellFont = [UIFont fontWithName:@"Helvetica" size:17.0];

    NSAttributedString *attributedText =
        [[NSAttributedString alloc]
            initWithString:cellText
            attributes:@
            {
                NSFontAttributeName: cellFont
            }];
    CGRect rect = [attributedText boundingRectWithSize:CGSizeMake(tableView.bounds.size.width, CGFLOAT_MAX)
                                               options:NSStringDrawingUsesLineFragmentOrigin
                                               context:nil];
    return rect.size.height + 20;
}
ddiego
  • 3,132
  • 1
  • 25
  • 18
4

A brief comment / answer to record my experience when I had the same problem. Despite using the code examples, the table view cell height was adjusting, but the label inside the cell was still not adjusting correctly - solution was that I was loading my cell from a custom NIB file, which happens after the cell height in adjusted.

And I had my settings inside the NIB file to not wrap text, and only have 1 line for the label; the NIB file settings were overriding the settings I adjusted inside the code.

The lesson I took was to make sure to always bear in mind what the state of the objects are at each point in time - they might not have been created yet! ... hth someone down the line.

Richard Le Mesurier
  • 27,993
  • 19
  • 127
  • 242
3

If we are to add only text in UITableView cell, we need only two delegates to work with (no need to add extra UILabels)

1) cellForRowAtIndexPath

2) heightForRowAtIndexPath

This solution worked for me:-

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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil)
    {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    cell.textLabel.font = [UIFont fontWithName:@"Helvetica" size:16];
    cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
    cell.textLabel.numberOfLines = 0;

    [cell setSelectionStyle:UITableViewCellSelectionStyleGray]; 
    cell.textLabel.text = [mutArr objectAtIndex:indexPath.section];
    NSLog(@"%@",cell.textLabel.text);

    cell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"arrow.png" ]];

    return cell;

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{  
    CGSize labelSize = CGSizeMake(200.0, 20.0);

    NSString *strTemp = [mutArr objectAtIndex:indexPath.section];

    if ([strTemp length] > 0)
        labelSize = [strTemp sizeWithFont: [UIFont boldSystemFontOfSize: 14.0] constrainedToSize: CGSizeMake(labelSize.width, 1000) lineBreakMode: UILineBreakModeWordWrap];

    return (labelSize.height + 10);
}

Here the string mutArr is a mutable array from which i am getting my data.

EDIT :- Here is the array which I took.

mutArr= [[NSMutableArray alloc] init];

[mutArr addObject:@"HEMAN"];
[mutArr addObject:@"SUPERMAN"];
[mutArr addObject:@"Is SUPERMAN powerful than HEMAN"];
[mutArr addObject:@"Well, if HEMAN is weaker than SUPERMAN, both are friends and we will never get to know who is more powerful than whom because they will never have a fight among them"];
[mutArr addObject:@"Where are BATMAN and SPIDERMAN"];
Atulkumar V. Jain
  • 5,062
  • 9
  • 42
  • 61
Arshad Parwez
  • 1,563
  • 2
  • 19
  • 29
3

Now the tableviews can have self-sizing cells. Set the table view up as follows

tableView.estimatedRowHeight = 85.0 //use an appropriate estimate tableView.rowHeight = UITableViewAutomaticDimension

Apple Reference

Jason
  • 385
  • 4
  • 10
1

I use the following solutions.

The data is provided separately in a member:

-(NSString *)getHeaderData:(int)theSection {
    ...
    return rowText;
}

The handling can be easily done in cellForRowAtIndexPath. Define the cell / define the font and assign these values to the result "cell". Note that the numberoflines is set to "0", which means take what is needed.

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

    UIFont *cellFont = [UIFont fontWithName:@"Verdana" size:12.0];
    cell.textLabel.text= [self getRowData:indexPath.section];
    cell.textLabel.font = cellFont;
    cell.textLabel.numberOfLines=0;
    return cell;
}

In heightForRowAtIndexPath, I calculate the heights of the wrapped text. The boding size shall be related to the width of your cell. For iPad this shall be 1024. For iPhone en iPod 320.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UIFont *cellFont = [UIFont fontWithName:@"Verdana" size:12.0];
    CGSize boundingSize = CGSizeMake(1024, CGFLOAT_MAX);
    CGSize requiredSize = [[self getRowData:indexPath.section] sizeWithFont:cellFont constrainedToSize:boundingSize lineBreakMode:UILineBreakModeWordWrap];
    return requiredSize.height;    
}
Atulkumar V. Jain
  • 5,062
  • 9
  • 42
  • 61
Vincent
  • 4,076
  • 1
  • 36
  • 37
1

I found this to be quite simple and straightForward :

[self.tableView setRowHeight:whatEvereight.0f];

for e.g. :

[self.tableView setRowHeight:80.0f];

This may or may not be the best / standard approach to do so, but it worked in my case.

Manish Kr. Shukla
  • 4,357
  • 1
  • 16
  • 33
  • As I understand it, the rowHeight property sets a fixed height that will be used for all cells in the table view. Using rowHeight is better for performance in large tables, but if you need the height of each cell to vary based on its contents then it seems tableView:heightForRowAtIndexPath: method has to be used. – jk7 Dec 23 '15 at 17:08
1

Try my code in swift . This code will work for normal UILabels also.

extension UILabel {
    func lblFunction() {
        //You can pass here all UILabel properties like Font, colour etc....
        numberOfLines = 0
        lineBreakMode = .byWordWrapping//If you want word wraping
        lineBreakMode = .byCharWrapping//If you want character wraping
    }
}

Now call simply like this

cell.textLabel.lblFunction()//Replace your label name 
iOS
  • 11,881
  • 4
  • 71
  • 92
0

I think this is a better and shorter solution. Just format the UILabel (textLabel) of the cell to auto calculate for the height by specifying sizeToFit and everything should be fine.

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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    // Configure the cell...
    cell.textLabel.text = @"Whatever text you want to put here is ok";
    cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
    cell.textLabel.numberOfLines = 0;
    [cell.textLabel sizeToFit];

    return cell;
}
Atulkumar V. Jain
  • 5,062
  • 9
  • 42
  • 61
dukz
  • 2,045
  • 2
  • 13
  • 5
-1

I don't think you can manipulate a base UITableViewCell's private UILabel to do this. You could add a new UILabel to the cell yourself and use numberOfLines with sizeToFit to size it appropriately. Something like:

UILabel* label = [[UILabel alloc] initWithFrame:cell.frame];
label.numberOfLines = <...an appriate number of lines...>
label.text = <...your text...>
[label sizeToFit];
[cell addSubview:label];
[label release];
Atulkumar V. Jain
  • 5,062
  • 9
  • 42
  • 61
drewh
  • 9,795
  • 7
  • 30
  • 41