Friday, August 11, 2017

Change The UITableViewCell Height According To Amount Of Text

In this post, I am going to explain how to change UITableViewCell height depending on the size of the text programmatically.

I have used XCode 8 and iOS 10 here.

Sometimes we, the developers, need to change or adjust the UITableViewCell height with the content. When the content size is fixed it is very easy to put it in the UITableView cell. You just need to set a fixed height for the cell. But this scenario is very rare. You really can not demand that the content size will be same for all cell. You have to think dynamically.

Let’s look at the Image 1. It is about the two most famous novels. If you notice you can see the amount of the text is different. First one is short and the last one is quite long.

Image 1 - Information Of Two famous novel

In UITableView if I put this two information with a fixed height then the cell will break like Image 2. The solution is not to use a fixed height. Use a dynamic height. That means you have to calculate the height for each cell. If you are familiar to the UITableView and its' delegates, you will notice that “heightForRowAtIndexPath” is called before “cellForRowAtIndexPath”. That means you will have the height of the cell first, then you are going to have the cell with its own value.

Image 2 - Second cell is breaking with fixed height
Now I am going to explain how I have calculated the dynamic height. First of all, I have used a UILabel in the UITableView cell (in “cellForRowAtIndexPath”). I have set the frame of UILabel same as the table view cell except for the origins (I have used 0 both for x and y ).
UILabel *cellLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, cellRect.size.width, cellRect.size.height)];
Now I will calculate the height (in "heightForRowAtIndexPath") of the table cell with the following given code.

CGRect textRect = [cellText boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:18.0f]} context:nil];
return textRect.size.height;


For details, you can check out this tutorial of mine. After implementing this code the table will look like Image 3.

Image 3 - Table view with dynamic cell height
Let’s make it a little complex. Suppose there are different font size and style have to be used for text like Image 4.

Image 4 - Novel information with different font size and style

In this scenario if I use above code snippet it look like image 5.

Image 5 - Both cell of UITableView are breaking after using different font size/style
The solution is simple. Just calculate the text height with their font size and style separately then combine them together and pass this combined value. Then it will look like Image 6. Look at the given code snippet below.

In heightForRowAtIndexPath add:
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
   float textlabelHeight=0.0;

   CGRect textRect;
   CGSize constraint = CGSizeMake(self.view.bounds.size.width, CGFLOAT_MAX);

   NSString *name=[NSString  stringWithFormat:@"%@",[[tableAry objectAtIndex:indexPath.row] valueForKey:@"Name"] ];

   /// Calculating the CGRect of novel name.
   textRect = [name boundingRectWithSize:constraint
   options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:26.0f]} context:nil];
   textlabelHeight = textlabelHeight + textRect.size.height;

   NSString *description=[[tableAry objectAtIndex:indexPath.row] valueForKey:@"Description"];

    /// Calculating the CGRect of description of novel.
   textRect = [description boundingRectWithSize:constraint
   options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:18.0f]} context:nil];
   textlabelHeight = textlabelHeight + textRect.size.height;

   /// Returning the summation of height of name and description
   return textlabelHeight;
}

In cellForRowAtIndexPath add:
-(UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    static NSString *contentTableIdentifier = @"Cell";
    UITableViewCell *cell;

    CGRect cellRect = [tableView rectForRowAtIndexPath:indexPath];
    CGRect myFrame=CGRectMake(0, 0, cellRect.size.width, cellRect.size.height);
    UILabel *myCustomLbl=[[UILabel alloc]initWithFrame:myFrame];

    if (cell == nil) {
      cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:contentTableIdentifier];
    }

    NSString *name=[NSString  stringWithFormat:@"%@\n",[[tableAry objectAtIndex:indexPath.row] valueForKey:@"Name"] ];
    NSMutableAttributedString *nameAttr=[[NSMutableAttributedString alloc]initWithString:name];
    [nameAttr setAttributes:@{NSFontAttributeName:[UIFont italicSystemFontOfSize:26.0f]} range:NSMakeRange(0, [name length])];

    NSString *description=[[tableAry objectAtIndex:indexPath.row] valueForKey:@"Description"];
    NSMutableAttributedString *descriptionAttr=[[NSMutableAttributedString alloc]initWithString:description];
    [descriptionAttr setAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:18.0f]} range:NSMakeRange(0, [description length])];

    NSMutableAttributedString *finalString = [[NSMutableAttributedString alloc]initWithAttributedString:nameAttr];
    [finalString appendAttributedString:descriptionAttr];
    myCustomLbl.attributedText = finalString;

    myCustomLbl.numberOfLines = 0;
    myCustomLbl.lineBreakMode = NSLineBreakByWordWrapping;
    [cell.contentView addSubview:myCustomLbl];

    return cell;
}

Note: the font size and style of text is same for both method “heightForRowAtIndexPath” and “cellForRowAtIndexPath"

Image 6 - TableView Looks good with different font size and style

Hopefully this tutorial helped you to understand the magic of dynamic cell height of UITableView.

No comments:

Post a Comment