58

I need to be able to adjust the height of a single cell in my UITableView so that it fits the amount of text in its detail label.

I have played with the following but it hasn't work for me:

How do I wrap text in a UITableViewCell without a custom cell

Attempted code:

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];
}

and

- (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;
}

This hasn't worked, it shows the entire string on the cell, however the cell height isn't affected at all.

halfer
  • 18,701
  • 13
  • 79
  • 158
Josh Kahane
  • 15,834
  • 40
  • 127
  • 241

9 Answers9

83

Its simple, just add this to your code:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return UITableViewAutomaticDimension;
}

It automatically count an height of row and than return a float... :-)

Hope this helps!

stepik21
  • 2,480
  • 3
  • 20
  • 30
  • 1
    Does it actually work with heightForRowAtIndexPath? It doesn't in my test. Also see UITableView.h: "Returning this value from tableView:heightForHeaderInSection: or tableView:heightForFooterInSection: results in a height that fits the value returned from tableView:titleForHeaderInSection: or tableView:titleForFooterInSection: if the title is not nil." I think it's only supposed to work with header/footer height. – Robert Kang Sep 29 '14 at 15:08
  • 5
    It is working, but only with basic "cell.textLabel" or others from Apple. It'could be harder to use with some custom cells... – stepik21 Sep 29 '14 at 18:08
  • BRILLIANT !! The only answer I've come across that actually REALLY works !!! You deserve a medal for this ! – Benjamin Nov 13 '14 at 13:27
  • Thank U! This also worked for me in heightForHeaderInSection method. In some cases I needed to return the value automatically. – Daniel Lima Jan 23 '15 at 23:07
  • 12
    only works for standard cells... not working for my custom cell. – zumzum May 27 '15 at 03:35
  • 3
    For me it worked with my custom cell, just need to add the constraints you want and it would work. – Fernando Mata Dec 05 '15 at 21:23
  • 4
    This works perfect only if provided an estimate value in the function estimatedHeightForRowAtIndexPath. This will not work well if you have an imageview and its set to aspect fit – LuAndre Feb 19 '16 at 05:49
  • 1
    I also have to add cell.textLabel.numberOfLines = 0 to make it work – Evan Mar 05 '16 at 05:21
  • This one even work with custom cell if you provide proper auto-layout to content of your cell. – AJPatel Aug 08 '16 at 07:26
57

Hi Josh,

Using tableView:heightForRowAtIndexPath: you can give the size of each row at run time. now your problem is how to get height from your string there are function in NSString class by this code your problem,

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
  NSString *str = [dataSourceArray objectAtIndex:indexPath.row];
    CGSize size = [str sizeWithFont:[UIFont fontWithName:@"Helvetica" size:17] constrainedToSize:CGSizeMake(280, 999) lineBreakMode:NSLineBreakByWordWrapping];
    NSLog(@"%f",size.height);
    return size.height + 10;
}

by below line you set your label`s num. of line to max. so set it in cellForRowAtIndexPath: method.

cell.textLabel.numberOfLines = 0;

if you use some custom cell then manage all label`s string with this and get sum of all that height then set the height of your cell.

Edit : iOS 8 onwards if you set proper autolayout constraints to label then you have to set only following delegate method to achieve this.

-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
   //minimum size of your cell, it should be single line of label if you are not clear min. then return UITableViewAutomaticDimension;    
   return UITableViewAutomaticDimension; 
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return UITableViewAutomaticDimension;
}

that`s it. no any calculation required. For more information check this tutorial.

AJPatel
  • 2,291
  • 22
  • 41
  • What does dataSourceArray hold? My text? – Josh Kahane Mar 22 '12 at 18:43
  • @JoshKahane yes it`s your table dataSource array from where you feed your table`s label. – AJPatel Mar 23 '12 at 04:07
  • 3
    `sizeWithFont` [has been deprecated](http://stackoverflow.com/a/18897897/2446155) – Andrew Jun 17 '14 at 19:22
  • Seems like for iOS 8.x and 9.x it has different behaviour, when I added different font size to label (size classes) for iOS 8.x cells have same height, not respecting label font size (on iPad font is bigger but cell height still same) – Injectios Dec 24 '15 at 12:44
  • 1
    adding **only** `UITableViewAutomaticDimension` in both `estimatedHeightForRowAtIndexPath` and `heightForRowAtIndexPath` works for me when using Custom style. :) – codelearner Apr 16 '17 at 08:18
48

In your CustomCell: Remember to add the constraint top and bottom for your UILabel

For adjust UILabel height depend on text just change UILabel line to 0 (see the answer here)

Then in your code, just only set 2 line

self.tableView.estimatedRowHeight = 80;
self.tableView.rowHeight = UITableViewAutomaticDimension;

Here is my custom cell
enter image description here Here is my UILabel constraints
enter image description here
The screen you will achieve

enter image description here

=== Suggestion ===
IF your cell has some UILabels and Images (not like my example) then:

  • You should put all UILabels and Images to one GroupView (View)
  • Add the constraint top and bottom to supper view for this GroupView (like the UILabel in my image)
  • Adjust UILabel height like the my suggestion above
  • Adjust the GroupView height depend on the content (the content is all UILabels and Images)
  • Finally, change estimateRowHeight and tableView.rowHeight like the code above

Hope this help

Community
  • 1
  • 1
Linh
  • 43,513
  • 18
  • 206
  • 227
  • What happens if one row has some UILabels and Images? – Brave Apr 06 '16 at 03:40
  • @Brave just put the `UILabels` and `Images` in a groupview (`View`). after that, you can think the groupview like one `UILabel`. config the constraint and modify code, like my instruction. i'm sure it will work – Linh Apr 06 '16 at 03:45
  • Labels have different length, images have different size. In a row, it is ordered by label1, image1, label2, image2. Can it work? – Brave Apr 06 '16 at 04:16
  • Of cource you can achive it ^^. here is my another question, it's is not a solution for you but it contains the image that I have achieve http://stackoverflow.com/questions/36420899/rounder-by-uibezierpath-issue-when-scrolling – Linh Apr 06 '16 at 04:23
  • Yeah! Thank you. Finally dynamic custom cell height for iOS. – myz Jun 03 '16 at 13:51
  • Hi, your code is really valuable. Thank you for sharing. However in my case due to design requirement tableviewcell has another UIView and that UIView contains the label. and I want to resize tableviewcell with that label as the content size is dynamic. Please help – Komal Gupta Aug 20 '20 at 11:10
22

Based on the code you have provided, I think you are increasing only the cell height and not the cell.textLabel's height.

Ideally, you should set the frame size of cell.textLabel and the cell for you to see the full text in the cell.

A neat way to see whats wrong with a view in terms of size, is to color it different than the background (try setting cell.textLabel background to yellow) and see if the height is actually being set.

Here's how it should be

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

    NSString *cellText = @"Go get some text for your cell.";
    UIFont *cellFont = cell.textLabel.font;
    CGSize constraintSize = CGSizeMake(280.0f, MAXFLOAT);
    CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
    cell.textlabel.frame.size = labelSize; 
    cell.text = cellText;
}

Hope this helps!

update: This is quite an old answer, and many lines in this answer may be deprecated.

Nitin Alabur
  • 5,654
  • 1
  • 30
  • 51
  • Tried this, trouble is, firstly you can't assign the cell label size, it throws an error and secondly, the label height appears to changing just fine and I can see all the text, however it is the cell which isn't getting larger. – Josh Kahane Mar 22 '12 at 19:22
  • Josh: The CGRect's size property is read only, and isnt specific to cell label. try CGRect frame = CGRectMake (xValue, yValue, width, calculatedHeight); cell.textLabel.frame = frame; – Nitin Alabur Mar 22 '12 at 19:48
  • Nope, the cell height doesn't change... Although what you just suggested does remove the un-assignable error. – Josh Kahane Mar 22 '12 at 19:56
  • It seems the `cellText` variable in the `heightForRowAtIndexPath` method that is in my question is effecting the cell height. If I change that it changes the cell height. – Josh Kahane Mar 22 '12 at 20:46
  • Thats what you want it to do right? I was thinking your cell height is not increasing no matter what text you put in there. – Nitin Alabur Mar 22 '12 at 20:53
  • It appeared that way initially. My next problem is that it gets shuffled after going into editing mode. Bugger, well I think I can deal with that, thanks for helping me fix this! – Josh Kahane Mar 22 '12 at 22:05
  • 1
    probably for that you can use [tableView reloadData] after it goes in to editing mode. It may recalculate based on the new cell frame. Also, in that case the CGSize constraintSize = CGSizeMake(280.0f, MAXFLOAT); may be messing it, because your hard setting the width as 280.0f – Nitin Alabur Mar 23 '12 at 17:22
  • Just in case anyone else reads this, make sure your font name is typed correctly capitalisation's and all as it caused me a big headache but not having that right breaks the whole thing! – Josh Kahane Mar 24 '12 at 14:11
  • 4
    ```UILineBreakModeWordWrap``` is deprecated in iOS 6.0, use ```NSLineBreakByWordWrapping``` instead – Ryan Wu May 27 '13 at 05:28
  • This won't work as you can only set this using tableView's rowHeight or heightForRowAtIndexPath. – mskw Dec 15 '13 at 18:48
7

For swift developers:

Custom cell: At first you can calculate the height of the text like below:

func calculateHeight(inString:String) -> CGFloat
    {
        let messageString = inString
        let attributes : [String : Any] = [NSFontAttributeName : UIFont.systemFont(ofSize: 15.0)]

        let attributedString : NSAttributedString = NSAttributedString(string: messageString, attributes: attributes)

        let rect : CGRect = attributedString.boundingRect(with: CGSize(width: 222.0, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)

        let requredSize:CGRect = rect
        return requredSize.height
    }

Set the width of your text label

Then call this function:

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        heightOfRow = self.calculateHeight(inString: conversations[indexPath.row].description)

        return (heightOfRow + 60.0)
}

For Basic cell:

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
           return UITableViewAutomaticDimension
    }

This function will not work for custom cells.

Hope it will work.

Rubaiyat Jahan Mumu
  • 2,741
  • 1
  • 25
  • 32
2

You can write a method globally to make it so it can be used throughout the app. You need to pass the text, font and width as per your requirement.

In Swift 4:

func heightForText(text: String,Font: UIFont,Width: CGFloat) -> CGFloat{

    let constrainedSize = CGSize.init(width:Width, height: CGFloat(MAXFLOAT))

    let attributesDictionary = NSDictionary.init(object: Font, forKey:NSAttributedStringKey.font as NSCopying)

    let mutablestring = NSAttributedString.init(string: text, attributes: attributesDictionary as? [NSAttributedStringKey : Any])

    var requiredHeight = mutablestring.boundingRect(with:constrainedSize, options: NSStringDrawingOptions.usesFontLeading.union(NSStringDrawingOptions.usesLineFragmentOrigin), context: nil)

    if requiredHeight.size.width > Width {
        requiredHeight = CGRect.init(x: 0, y: 0, width: Width, height: requiredHeight.height)

    }
    return requiredHeight.size.height;
}
Michael
  • 2,758
  • 6
  • 28
  • 55
Harry P
  • 21
  • 7
2

In tableView:heightForRowAtIndexPath: you can take the text and use sizeWithFont:constrainedToSize: in order to get the size of the text.

Then just return the height plus some extra spacing for buffer.

Kevin Low
  • 2,632
  • 13
  • 15
0

I was able to get this accomplished using autolayout. Make sure your label snaps to the top and bottom of the cell (I'm using a prototype cell), and it's lines set to 0. Then in the tableView:heightForRowAtIndexPath:sizeWithFont:constrainedToSize: you can set the height of the cell by doing the calculation on the text size:

NSString *key = self.detailContent.allKeys[indexPath.row];
NSDictionary *dictionary = self.detailContent[key];
NSString *cellText = dictionary[kSMDetailTableViewCellTextKey];
UIFont *cellFont = [UIFont fontWithName:kFontKeyEmondsans size:12.0];
CGSize constraintSize = CGSizeMake(252.0f, MAXFLOAT);
CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping];
return labelSize.height;// + 10;
VaporwareWolf
  • 9,573
  • 8
  • 48
  • 78
  • What protocol is `tableView:heightForRowAtIndexPath:sizeWithFont:constrainedToSize:` from? `tableView:heightForRowAtIndexPath:` is in `UITableViewDelegate`, but the sizeWithFont part doesn't sound like it's from an Apple UITableView protocol. `sizeWithFont` from the UIKit category on NSString was deprecated in iOS 7 if that's what this is referring to. – stuckj Mar 17 '16 at 20:39
0
NSString *str;
NSArray* dictArr;

if (_index==0) {
    dictArr = mustangCarDetailDictArr[indexPath.section];

}

NSDictionary* dict = dictArr[indexPath.row];


if (indexPath.section ==0)
{

    str = [dict valueForKey:@"FeatureName"];
    if ([[dict valueForKey:@"FeatureDetail"] isKindOfClass:[NSString class]])
    {

        str = [dict valueForKey:@"FeatureDetail"];



    }
    else
    {
        if (dictArr.count>indexPath.row+1)
        {
            NSDictionary* dict2 = dictArr[indexPath.row+1];
            if ([[dict2 valueForKey:@"FeatureDetail"] isKindOfClass:[NSString class]])
            {


            }
        }

    }


}
CGSize size = [str sizeWithFont:[UIFont fontWithName:@"Helvetica" size:17] constrainedToSize:CGSizeMake(280, 999) lineBreakMode:NSLineBreakByWordWrapping];
NSLog(@"%f",size.height);
return size.height + 20;


}
NSNoob
  • 5,320
  • 5
  • 35
  • 51