20
votes

I have a UITableview with multiple reusable TableViewCells. In one cell I have a UITextView, that resizes itself to fit its content. Now I "just" have to resize the contentView of the TableViewCell, so I can read the while text. I already tried:

cell2.contentView.bounds.size.height = cell2.discriptionTextView.bounds.size.height; 

Or:

cell2.contentView.frame = CGRectMake(0, cell2.discriptionTextView.bounds.origin.y,     
cell2.discriptionTextView.bounds.size.width,     
cell2.discriptionTextView.bounds.size.height); 

In the method:

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

But it won't work.

Does anyone know how to do this?

New code:

    @implementation AppDetail

    CGFloat height;
    …

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

    cell2.TextView.text = self.text;
            [cell2.TextView sizeToFit];
            height = CGRectGetHeight(cell2.TextView.bounds);
     …
    }

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

        if (indexPath.row == 0) {
            return 143;
        }
        if (indexPath.row == 1) {
            return height;
        }

        return 0;
    }
8

8 Answers

18
votes

You can only resize a UITableViewCell in tableView:heightForRowAtIndexPath: delegate method.

You have to estimate what the size of the text will be when that method is called for every row when the tableView is loaded.

This is what I did to solve the problem.

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
 {
     NSString * yourText = self.myArrayWithTextInIt[indexPath.row]; // or however you are getting the text
     return additionalSpaceNeeded + [self heightForText:yourText];
 }

 -(CGFloat)heightForText:(NSString *)text
 {
   NSInteger MAX_HEIGHT = 2000;
   UITextView * textView = [[UITextView alloc] initWithFrame: CGRectMake(0, 0, WIDTH_OF_TEXTVIEW, MAX_HEIGHT)];
   textView.text = text;
   textView.font = // your font
   [textView sizeToFit];
   return textView.frame.size.height;
  }

EDIT

While I used this solution for a while, I found a more optimal one that I would recommend using as it doesn't require allocating an entire textView in order to work, and can handle text greater than 2000.

-(CGFloat)heightForTextViewRectWithWidth:(CGFloat)width andText:(NSString *)text
{
    UIFont * font = [UIFont systemFontOfSize:12.0f];

    // this returns us the size of the text for a rect but assumes 0, 0 origin
    CGSize size = [text sizeWithAttributes:@{NSFontAttributeName: font}];

    // so we calculate the area
    CGFloat area = size.height * size.width;

    CGFloat buffer = whateverExtraBufferYouNeed.0f;

    // and then return the new height which is the area divided by the width
    // Basically area = h * w
    // area / width = h
    // for w we use the width of the actual text view
    return floor(area/width) + buffer;
}
16
votes

As @Rob Norback said, There is something called UITableViewAutomaticDimension.

For Swift, The easiest way to resize content from UITableViewCell on the fly is to just add this.

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}

override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
     return UITableViewAutomaticDimension
}
14
votes

Here's an updated version for iOS 7+ that is cleaner (no extra method)

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        UIFont * font = [UIFont systemFontOfSize:15.0f];
        NSString *text = [getYourTextArray objectAtIndex:indexPath.row];
        CGFloat height = [text boundingRectWithSize:CGSizeMake(self.tableView.frame.size.width, maxHeight) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) attributes:@{NSFontAttributeName: font} context:nil].size.height;

        return height + additionalHeightBuffer;
    }
8
votes

You need you implement heightForRowAtIndexPath.

Say that the data that is to be displayed in the textView is stored in a NSArray.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
 CGFloat cellheight = 30; //assuming that your TextView's origin.y is 30 and TextView is the last UI element in your cell

 NSString *text = (NSString *)[textArray objectAtIndex:indexpath.row];
 UIFont *font = [UIFont systemFontOfSize:14];// The font should be the same as that of your textView
 CGSize constraintSize = CGSizeMake(maxWidth, CGFLOAT_MAX);// maxWidth = max width for the textView

 CGSize size = [text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];

 cellHeight += size.height; //you can also add a cell padding if you want some space below textView

}
4
votes

I favor this solution of Jure

  • First, set constraints of textview to be pinned with its superview (cell's contentView in this case).
  • Disable textView.scrollEnabled
  • Set

    table.rowHeight = UITableViewAutomaticDimension;
    tableView.estimatedRowHeight = 44;
    

    If finally, your code not works, then use this instead

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        return UITableViewAutomaticDimension;
    }
    
    - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
        return 44;
    }
    
  • Implement UITextViewDelegate like this:

    - (void)textViewDidChange:(UITextView *)textView {
         CGPoint currentOffset = self.tableView.contentOffset;
         [UIView setAnimationsEnabled:NO];
         [self.tableView beginUpdates];
         [self.tableView endUpdates];
         [UIView setAnimationsEnabled:YES];
         [self.tableView setContentOffset:currentOffset animated:NO];
      }
    
3
votes

This thread has been quite a while, but in iOS 8 UITableViewAutomaticDimension was introduced. You have to set constraints from the top to the bottom of the cell like a scroll view to make this work. But after that, just add the following code to viewDidLoad():

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 122.0

Make sure your estimated height is as close as possible to the real thing otherwise you'll get some buggy scrolling.

2
votes

Adding these two methods to the ViewController with UITableViewAutomaticDimension should do the trick. It has worked for me when embedding a UITextView inside of a UITableViewCell with variable length text.

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

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return UITableViewAutomaticDimension;
}
1
votes

In case of UILabel subview in the UITableViewCell, I accomplished auto resize of the label just by setting the label's constraints (using storyboard, Xcode 8.3.2).

This is working since apparently the label's and the cell's default behavior is sizeToFit. Same should work for UITextView as well.