11

I have implemented TableView with CustomCell in my app,

I want dynamic height of my UITableViewCell according to text length in UITableViewCell,

here is the snapshot of Customcell

: and here is the snapshot of my UITableView : code snippet for heightForRowAtIndexPath

#define FONT_SIZE 14.0f
#define CELL_CONTENT_WIDTH 320.0f
#define CELL_CONTENT_MARGIN 10.0f

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
    NSString *text = [DescArr objectAtIndex:[indexPath row]];
    CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);
    CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
    CGFloat height = MAX(size.height, 100.0);
    return height; 
}

As you can see in the 2nd image, height for cell is fixed, it doesn't change with it's text (content) size.

Where am I making mistake? How can I make a a label or a cell to update its size according to its contents/text?

Krunal
  • 77,632
  • 48
  • 245
  • 261
Krunal
  • 6,440
  • 21
  • 91
  • 155
  • Images are missing?? :) – NHS Aug 30 '13 at 07:26
  • You code snippet for height calculation looks correct, did you setup the delegate for the table view correctly? also try and log the size: `NSLog(@"Hieght: %f", height);` and see if the height is correct. – rckoenes Aug 30 '13 at 07:31
  • yes, i have connected delegate properly and my log shows diffrent heights `54.0`, `36.0`, `18.0` – Krunal Aug 30 '13 at 07:33
  • 1
    CGFloat height = MAX(size.height, 100.0); should be 100 to 55 or default cell height.. – SachinVsSachin Aug 30 '13 at 07:57
  • @Sachin: your suggestion is correct after changing to 55 cell height is proper but labels are not displayed in center. when i log my cell height in `cellForRowAtIndexPath` NSLog(@"cell.frame.size.height=%f",cell.frame.size.height); it shows height as `100` in all cell – Krunal Aug 30 '13 at 08:07
  • self.tableView.rowHeight = UITableViewAutomaticDimension; self.tableView.estimatedRowHeight = (your row height); these 2 lines of code will help you to set RowHeight automatically depends on content of row. – RashmiG Mar 12 '16 at 15:59
  • Here's the sample code in swift 2.3 https://github.com/dpakthakur/DynamicCellHeight – Deepak Thakur Dec 17 '16 at 14:08
  • Have you created class for your custom cell, inheriting UITableViewCell? I was having the same issue, when I created custom cell of UITableViewCell and associated that class with my view in Storyboard, it worked for me. – Nuzhat Zari Jan 29 '17 at 09:49

12 Answers12

13

Please, Look HERE - Dynamic Table View Cell Height and Auto Layout tutorial.

What you need:

  • set required constraint on elements in cell (make shure that all done correctly, if no - you can get a lot of problem). Also make shure that you set IntrinsicSize to PlaceHolder value

enter image description here

  • add few method for calculating size of cell

Methods:

//this will calculate required height for your cell
-(CGFloat)heightForBasicCellAtIndexPath:(NSIndexPath *)indexPath {
      static UIYourClassCellName *sizingCell = nil;
      //create just once per programm launching
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
      sizingCell = [self.tableView dequeueReusableCellWithIdentifier:@"identifierOfCell"];
});
  [self configureBasicCell:sizingCell atIndexPath:indexPath];
  return [self calculateHeightForConfiguredSizingCell:sizingCell];
}
//this method will calculate required height of cell
- (CGFloat)calculateHeightForConfiguredSizingCell:(UITableViewCell *)sizingCell {
      [sizingCell setNeedsLayout];
      [sizingCell layoutIfNeeded];
      CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
      return size.height;
}

And call

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  return [self heightForBasicCellAtIndexPath:indexPath];
}

Configuration of cell

- (void)configureBasicCell:(RWBasicCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    //make some configuration for your cell
}

After all operation i got next (text inside cell only as placeholder):

enter image description here

hbk
  • 10,908
  • 11
  • 91
  • 124
6

The following code worked fine for me.Try with this

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    CGFloat lRetval = 10;
    CGSize maximumLabelSize = CGSizeMake(231, FLT_MAX);
    CGSize expectedLabelSize;


    CGFloat numberoflines = [thirdcellText length]/17.0;

    if (indexPath.section == 0) {
        expectedLabelSize = [firstcellText sizeWithFont:[UIFont systemFontOfSize:16.0]
                                      constrainedToSize:maximumLabelSize
                                          lineBreakMode:NSLineBreakByWordWrapping];
        lRetval = expectedLabelSize.height;
    }
    else if(indexPath.section == 1)
    {
        expectedLabelSize = [secondcellText sizeWithFont:[UIFont systemFontOfSize:16.0]
                                       constrainedToSize:maximumLabelSize
                                           lineBreakMode:NSLineBreakByWordWrapping];
        lRetval = expectedLabelSize.height;
    }
    else if (indexPath.section == 2)
    {
        expectedLabelSize = [thirdcellText sizeWithFont:[UIFont systemFontOfSize:16.0]
                                       constrainedToSize:CGSizeMake(231, numberoflines*17.0)
                                           lineBreakMode:NSLineBreakByWordWrapping];
        lRetval = expectedLabelSize.height-128.0;
    }

    UIImage *factoryImage = [UIImage imageNamed:NSLocalizedString(@"barcode_factory_reset.png", @"")];

    CGFloat height = factoryImage.size.height;

    if (lRetval < height) {
        lRetval = height+15.0;
    }

    return lRetval;
}

Try to add the following code in your customcell class autolayout method

textview.frame = frame;
CGRect frame1 = textview.frame;
frame1.size.height = textview.contentSize.height-2;
textview.frame = frame1;


textview.contentSize = CGSizeMake(textview.frame.size.width, textview.frame.size.height);

labelPtr.frame = CGRectMake(CGRectGetMinX(imageView.frame)+CGRectGetMaxX(imageView.frame)+5.0, textview.frame.size.height+10.0, 140, 16.0);
[labelPtr setNeedsDisplayInRect:labelPtr.frame];

Try to set the label properties like the following

labelPtr = [[UILabel alloc] initWithFrame:CGRectZero];
labelPtr.backgroundColor =[UIColor clearColor];
[labelPtr setNeedsLayout];
[labelPtr setNeedsDisplay];
[self.contentView addSubview:labelPtr];
Aamir
  • 16,329
  • 10
  • 59
  • 65
NHS
  • 409
  • 2
  • 7
  • Dont worry with thirdcellText its a nsstring with text init – NHS Aug 30 '13 at 07:29
  • where to intialize that thirdcellText? – Krunal Aug 30 '13 at 07:33
  • if you have text for which height should be calculated then you can retrieve and assign to thirdcellText.you can assign the text in heightforrowatindexpath: method only.. – NHS Aug 30 '13 at 07:35
  • if you have textview for the text you are adding then the above code will be useful. – NHS Aug 30 '13 at 07:48
  • I am using Label not TextView – Krunal Aug 30 '13 at 07:54
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/36513/discussion-between-nhs-and-krunal) – NHS Aug 30 '13 at 08:34
  • [firstcellText sizeWithFont:[UIFont systemFontOfSize:16.0] constrainedToSize:maximumLabelSize lineBreakMode:NSLineBreakByWordWrapping]; This method seems depricated. Follow "http://stackoverflow.com/a/22742610/3021582" – Taranmeet Singh Jul 15 '14 at 11:49
5

A'm looking for a long time how to determinate cell height properly, - looks like - it's a best solution, boundingRectWithSize and constrainedToSize often incorrectly calculated text height, you need to create UILabel than use sizeThatFits function, see below

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {

    UILabel  * label = [[UILabel alloc] initWithFrame:CGRectMake(8, 5, celllabelWidth, 9999)];
    label.numberOfLines=0;
    label.font = [UIFont fontWithName:fontName size:textSize];
    label.text = @"celllabelTextHere";

    CGSize maximumLabelSize = CGSizeMake(celllabelWidth, 9999);
    CGSize expectedSize = [label sizeThatFits:maximumLabelSize];
    return expectedSize.height;
}
user2154220
  • 83
  • 1
  • 8
5

I saw a lot of solutions but all was wrong or uncomplet. You can solve all problems with 5 lines in viewDidLoad and autolayout. This for objetive C:

_tableView.delegate = self;
_tableView.dataSource = self;
self.tableView.estimatedRowHeight = 80;//the estimatedRowHeight but if is more this autoincremented with autolayout
self.tableView.rowHeight = UITableViewAutomaticDimension;
[self.tableView setNeedsLayout];
[self.tableView layoutIfNeeded];
self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0) ;

For swift 2.0:

 self.tableView.estimatedRowHeight = 80
 self.tableView.rowHeight = UITableViewAutomaticDimension      
 self.tableView.setNeedsLayout()
 self.tableView.layoutIfNeeded()
 self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0)

Now create your cell with xib or into tableview in your Storyboard With this you no need implement nothing more or override. (Don forget number os lines 0) and the bottom label (constrain) downgrade "Content Hugging Priority -- Vertical to 250"

enter image description here enter image description here

You can donwload the code in the next url: https://github.com/jposes22/exampleTableCellCustomHeight

References: http://candycode.io/automatically-resizing-uitableviewcells-with-dynamic-text-height-using-auto-layout/

Jose Pose S
  • 1,175
  • 17
  • 33
  • I am working with swift and your solution works perfect on device supporting ios 9.0. However when ran it on a device with ios7.1 the cells go invisible. Could you @Jose Pose S help me. – Cloy Mar 02 '16 at 13:17
  • I´m so sorry, I can´t help you for iOS 7 :(, because from iOS 8 Autolayout was changed. You can use this with Objective-C or swift but with iOS 8 or more. – Jose Pose S Mar 03 '16 at 15:52
  • Here's the sample code in swift 2.3 https://github.com/dpakthakur/DynamicCellHeight – Deepak Thakur Dec 17 '16 at 14:09
  • The code is the same for all versions of Swift and iOS 8 or higher – Jose Pose S Jan 25 '17 at 12:14
5

This is very simple now
Use below steps

  1. Set constraint to your Label (If using custom cell)
  2. Number of line must be 0
  3. Setup few properties of UITableView

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

Enjoy:)
For more detail you can check
www.raywenderlich.com
stackoverflow.com

Community
  • 1
  • 1
Tarun Seera
  • 4,212
  • 4
  • 27
  • 41
3

Could you try this;

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
     int topPadding = cell.yourLabel.frame.origin.x;
     int bottomPadding = cell.frame.size.heigth-(topPadding+cell.yourLabel.frame.size.height);
     NSString *text = [DescArr objectAtIndex:[indexPath row]];
     CGSize maximumSize = CGSizeMake(cell.yourLabel.frame.size.width, 9999);
     CGSize expectedSize = [text sizeWithFont:yourCell.yourLabel.font constrainedToSize:maximumSize lineBreakMode:yourCell.yourLabel.lineBreakMode];

     return topPadding+expectedSize.height+bottomPadding;
}
Paresh Navadiya
  • 38,095
  • 11
  • 81
  • 132
Engnyl
  • 1,380
  • 16
  • 24
2

Refer this link you are using Autolayout

else you can use below approach

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {


NewsVCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

if (cell == nil)
{

    cell = [[NewsVCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];

}

cell.titleCell.numberOfLines = 0;
cell.descriptionCell.numberOfLines = 0;

cell.titleCell.font = [UIFont systemFontOfSize:12.0f];
cell.descriptionCell.font = [UIFont systemFontOfSize:12.0f];

cell.descriptionCell.textColor = [UIColor lightGrayColor];


CGSize maximumLabelSize;

if([UIDevice currentDevice].userInterfaceIdiom==UIUserInterfaceIdiomPad || [[[UIDevice currentDevice] model] isEqualToString:@"iPad Simulator"])
{
    maximumLabelSize = CGSizeMake(768, 10000);

}
else
{
    maximumLabelSize = CGSizeMake(270, 10000);

}

NSString *newsTitle =  [[feeds objectAtIndex:indexPath.row] objectForKey: @"title"];

NSString *descriptionsText = [[feeds objectAtIndex:indexPath.row] objectForKey: @"description"];


CGSize expectedTitleLabelSize = [newsTitle sizeWithFont: cell.titleCell.font constrainedToSize:maximumLabelSize lineBreakMode:cell.titleCell.lineBreakMode];

CGSize expectedDescriptionLabelSize = [descriptionsText sizeWithFont:cell.descriptionCell.font constrainedToSize:maximumLabelSize lineBreakMode:cell.descriptionCell.lineBreakMode];

NSLog(@"cellForRowAtIndexPath :indexpath.row %d: height expectedTitleLabelSize:%f , indexpath.row height expectedDescriptionLabelSize:%f",indexPath.row,expectedTitleLabelSize.height,expectedDescriptionLabelSize.height);



if (newsTitle.length > 0)
{

    cell.titleCell.frame = CGRectMake(20.0f, 10.0f, 270.0f ,expectedTitleLabelSize.height+20.0f);

}
else
{
     cell.titleCell.frame = CGRectMake(20.0f, 10.0f, 270.0f ,expectedTitleLabelSize.height-20.0f);
}


if (descriptionText.length > 0)
{
    cell.descriptionCell.frame =  CGRectMake(20.0f, 10.0f + cell.titleCell.frame.size.height, 270.0f, expectedDescriptionLabelSize.height+20.0f);

}
else
{
    cell.descriptionCell.frame =  CGRectMake(20.0f, cell.titleCell.frame.size.height, 270.0f, 0.0f);

}


  cell.descriptionCell.frame =  CGRectMake(20.0f, 10.0f + cell.titleCell.frame.size.height, 270.0f, expectedDescriptionLabelSize.height+20.0f);

cell.titleCell.text = newsTitle;
cell.descriptionCell.text = descriptionsText;

NSLog(@"indexpath.row %d :title %@ ",indexPath.row,newsTitle);

NSLog(@"indexpath.row %d :description %@",indexPath.row,descriptionsText);

return cell;

 }

pragma mark - UITableViewDelegate

   - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
 {
float totalHeight = 0.0f;

UILabel *labelTitle;


CGSize maximumLabelSize;


if([UIDevice currentDevice].userInterfaceIdiom==UIUserInterfaceIdiomPad || [[[UIDevice currentDevice] model] isEqualToString:@"iPad Simulator"])
{
    labelTitle = [[UILabel alloc]initWithFrame:CGRectMake(0.0f, 0.0f, 692.0f, 20.0f)];  // iPad
    maximumLabelSize = CGSizeMake(768.0f, 10000.0f);

}
else
{
    labelTitle = [[UILabel alloc]initWithFrame:CGRectMake(0.0f, 0.0f, 270.0f, 20.0f)];
    maximumLabelSize = CGSizeMake(270.0f, 10000.0f);

}



labelTitle.font = [UIFont systemFontOfSize:12.0f];


NSString *newsTitle;
NSString *newsDescription;

  //  cell.titleCell.text = [[feeds objectAtIndex:indexPath.row] objectForKey: @"title"];
  //   cell.descriptionCell.text = [[feeds objectAtIndex:indexPath.row] objectForKey: @"description"];



    newsTitle = [[feeds objectAtIndex:indexPath.row] objectForKey: @"title"];

    newsDescription = [[feeds objectAtIndex:indexPath.row] objectForKey: @"description"];

NSLog(@"indexpath.row %d :newsDescription.length %d",indexPath.row,newsDescription.length);
CGSize expectedTitleLabelSize;
CGSize expectedDescriptionLabelSize;


if (newsTitle.length > 0)
{
    expectedTitleLabelSize = [newsTitle sizeWithFont:labelTitle.font constrainedToSize:maximumLabelSize lineBreakMode:labelTitle.lineBreakMode];
    totalHeight = totalHeight + 20.0f;
}
else
{
    expectedTitleLabelSize = CGSizeMake(0.0f, 0.0f);
    totalHeight = -20.0f;
}

if (newsDescription.length > 0)
{
    expectedDescriptionLabelSize = [newsDescription sizeWithFont:labelTitle.font constrainedToSize:maximumLabelSize lineBreakMode:labelTitle.lineBreakMode];
    totalHeight = totalHeight + 20.0f;

}
else
{
    expectedDescriptionLabelSize = CGSizeMake(0.0f, 0.0f);
    totalHeight = -20.0f;
}


//  NSLog(@"question: %f title:%f",expectedQuestionLabelSize.height,expectedTitleLabelSize.height);

totalHeight = expectedDescriptionLabelSize.height + expectedTitleLabelSize.height + 30.0f+20.0f;




return totalHeight;

 }
Community
  • 1
  • 1
bhavya kothari
  • 7,484
  • 4
  • 27
  • 53
2

To set automatic dimension for row height & estimated row height, ensure following steps to make, auto dimension effective for cell/row height layout.

  • Assign and implement tableview dataSource and delegate
  • Assign UITableViewAutomaticDimension to rowHeight & estimatedRowHeight
  • Implement delegate/dataSource methods (i.e. heightForRowAt and return a value UITableViewAutomaticDimension to it)

-

@IBOutlet weak var table: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()

    // Don't forget to set dataSource and delegate for table
    table.dataSource = self
    table.delegate = self

    // Set automatic dimensions for row height
    // Swift 4.2 onwards
    table.rowHeight = UITableView.automaticDimension
    table.estimatedRowHeight = UITableView.automaticDimension


    // Swift 4.1 and below
    table.rowHeight = UITableViewAutomaticDimension
    table.estimatedRowHeight = UITableViewAutomaticDimension

}



// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    // Swift 4.2 onwards
    return UITableView.automaticDimension

    // Swift 4.1 and below
    return UITableViewAutomaticDimension
}

For label instance in UITableviewCell

  • Set number of lines = 0 (& line break mode = truncate tail)
  • Set all constraints (top, bottom, right left) with respect to its superview/ cell container.
  • Optional: Set minimum height for label, if you want minimum vertical area covered by label, even if there is no data.

enter image description here

Krunal
  • 77,632
  • 48
  • 245
  • 261
1

If you want to constrain the maximum height to 100 pt you have to use MIN instead on MAX:

CGFloat height = fmin(size.height, 100.0);
Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
0

I needed a dynamic table view cell height based on the amount of text to be displayed in that cell. I solved it in this way:

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (!isLoading)
        {

            if ([self.conditionsDataArray count]>0)
            {
                Conditions *condition =[self.conditionsDataArray objectAtIndex:indexPath.row];
                int height;
                UITextView *textview = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 236, 0)];   //you can set your frame according to your need
                textview.text  = condition.comment;
                textview.autoresizingMask = UIViewAutoresizingFlexibleHeight;
                [tableView addSubview:textview];
                textview.hidden = YES;
                height = textview.contentSize.height;
                NSLog(@"TEXT VIEW HEIGHT %f", textview.contentSize.height);
                [textview removeFromSuperview];
                [textview release];
                return height;
       }
       return 55;  //Default height, if data is in loading state
}

Notice that the Text View has been added as Subview and then made hidden, so make sure you add it as SubView otherwise it's height will not be considered.

allprog
  • 16,540
  • 9
  • 56
  • 97
Jamal Zafar
  • 2,179
  • 2
  • 27
  • 32
  • Whooooa. Don't do this. This method should be fast like hell and shouldn't contain any view hierarchy modification. – allprog Aug 30 '13 at 07:56
  • @allprog: No compromise on performance, i have successfully used this method and it is working smoothly in my app – Jamal Zafar Aug 30 '13 at 08:16
  • If you say so. I haven't tried it yet but you made me curious. UITableView seems to be kind of broken in this respect as it asks for the height and then the contents. I'm sure this is due to some optimization but in the current case it requires the calculation two times. – allprog Aug 30 '13 at 08:31
  • Here's the sample code in swift 2.3 https://github.com/dpakthakur/DynamicCellHeight – Deepak Thakur Dec 17 '16 at 14:09
0

I just wrote about this problem and the approach I finally chose. You can read about it here: Dynamic UITableView Cell Height Based on Contents

Basically, I created a UITableView subclass that handles all this for both default and custom cells. It probably needs tweaking, but I have used it as is with good result.

You can grab the code here: https://github.com/danielsaidi/DSTableViewWithDynamicHeight

Hope it helps (...and if it didn't, I apologize and would love to hear why not)

Community
  • 1
  • 1
Daniel Saidi
  • 6,079
  • 4
  • 27
  • 29
0

Try This, It worked like a charm! for me,

In viewDidLoad write this code,

-(void)viewDidLoad 
{
[super viewDidLoad];
 self.tableView.estimatedRowHeight = 100.0; // for example. Set your average height
 self.tableView.rowHeight = UITableViewAutomaticDimension;
}

In cellForRowAtIndexPath write this code,

 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
 {
  static NSString *CellIdentifier = @"Cell";
  UITableViewCell *cell = [tableView 
  dequeueReusableCellWithIdentifier:CellIdentifier];
 if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] ;
  }
    cell.textLabel.numberOfLines = 0; // Set label number of line to 0
    cell.textLabel.text=[[self.arForTable objectAtIndex:indexPath.row] valueForKey:@"menu"];
    [cell.textLabel sizeToFit]; //set size to fit 
    return cell;
 }

Hopes so this will help for some one .

Jaywant Khedkar
  • 5,941
  • 2
  • 44
  • 55