How to resize UITableViewCell to fit its content? - ios

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

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

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
}

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

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
}

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

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.

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

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.

Related

Increase height of tableview cell according to amount of UILabel text

I want to change the height of my tableview cell according to the amount of text by using auto layout. I have tried the following code but it doesn't work:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
AddTaskDetails *addTaskDetail = (self.tasksArray)[indexPath.row];
CGFloat height;
float textcount = [addTaskDetail.taskDetail length];
if(textcount>60)
{
height = textcount-20;
NSLog(#"%d,%f",indexPath.row,height);
}
else
{
height = 70;
}
return height;
}
You better don't hardcode the height required for the string. Rather use the attributed text height property.
let attributes = [NSFontAttributeName : textFont,
NSForegroundColorAttributeName : UIColor(
red:25/255,
green:176/255,
blue:37/255,
alpha:1.0)]
let attrString:NSAttributedString? = NSAttributedString(string: yourString, attributes: attributes)
let rect:CGRect = attrString!.boundingRectWithSize(CGSizeMake(280.0,CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, context:nil )
return rect.height
You will need to use boundingRectWithSize:options:attributes:context on the string to be rendered.
This is a frequently asked question and you may find code snippets when you search for 'UITableViewCell with dynamic height'.
Try with this...
it will help you.
NSString *classSubjecttxt =#"Some text";
CGSize requiredSizeSubjetc =[classSubjecttxt sizeWithFont:[UIFont fontWithName:#"Trebuchet MS" size:12] constrainedToSize:CGSizeMake(labelwidth, CGFLOAT_MAX)];
int height=YOUR DEFAULT HEIGHT;
if(requiredSizeSubjetc.height >18){
height=height-18+ceil(requiredSizeSubjetc.height);
}
return height;
You don't have to do it programmatically. You can easily do it using Autolayouts in interface builder.
Just add a UITableViewCell to you UITableView
Set its style to custom
Make sure its size in Size inspector is default
Add a UILabel to this cell
Set its Top, Bottom, Left, Right Constraints
In size inspector set preferred width to explict
In attribute inspector set number of lines to "0"
Then add these lines in viewDidLoad()
tableView.estimatedRowHeight = 40.0
tableView.rowHeight = UITableViewAutomaticDimension
Also implement these delegate methods
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 44
}
- (CGFloat)getLabelHeight:(NSString*)textvalue
{
if (![textvalue isEqualToString:#""]) {
NSString *string=textvalue;
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
UIFont *font = [UIFont systemFontOfSize:12];
CGSize constraint = CGSizeMake(SCREENWIDTH/1.0,NSIntegerMax);
NSDictionary *attributes = #{NSFontAttributeName: font};
CGRect rect = [string boundingRectWithSize:constraint
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:attributes
context:nil];
return rect.size.height;
}
else
{
return 20;
}
}
Then You can get size by calling this :
AddTaskDetails *addTaskDetail = (self.tasksArray)[indexPath.row];
CGFloat textHeight = [self getLabelHeight:[addTaskDetail valueForKey:#"YOURKEY"]];
Here You will get the text size of your text then return it to heightForRowAtIndexPath.
First of all you need to calculate height of your label.
You can get dynamic height of your label by calling with below functions:
-(CGFloat)getDynamicHeightOfLabelWithFont:(UIFont *)font withText:(NSString *)text withFrame:(CGRect)initialFrame
{
UILabel *lblDummy = [[UILabel alloc] initWithFrame:initialFrame];
lblDummy.font = font;
lblDummy.lineBreakMode = NSLineBreakByWordWrapping;
lblDummy.numberOfLines = 0;
lblDummy.text = text;
CGRect dummyFrame = initialFrame;
dummyFrame.size = [lblDummy sizeThatFits:initialFrame.size];
return dummyFrame.size.height;
}
You need to call this function on heightForRowAtIndexPath and return the height.
and you need to set the frame on cellForRowAtIndexPath and set frame to your label.
1.Create a custom cell class. Create outlets for label/imageview.
2.Add this method in your custom cell class.
-(void) layoutSubviews
{
[super layoutSubviews];
[self.contentView layoutIfNeeded];
self.yourLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.sentenceLabel.frame);
}
3.In your view controller class,create a property of your custom cell class.
-(DynamicTblVCell *)prototypeCell
{
if(!_prototypeCell)
{
_prototypeCell = [self.tableView dequeueReusableCellWithIdentifier:#"DynamicTblVCell"];
}
return _prototypeCell;
}
4. In your viewDidLoad add these two lines:
self.tableView.estimatedRowHeight = 100.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;
5. And finally do this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"DynamicTblVCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
//to configure a cell before it is displayed
[self configureCell:cell forRowAtIndexPath:indexPath];
return cell;
}
-(void)configureCell:(UITableViewCell *)cell forRowAtIndexPath: (NSIndexPath *)indexPath
{
if([cell isKindOfClass:[DynamicTblVCell class]])
{
DynamicTblVCell * textCell = (DynamicTblVCell *)cell;
textCell.sentenceLabel.text = [NSString stringWithFormat:#"jhjshdjshdjhkjdhajsdhajsdh"];
textCell.sentenceLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
}
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
[self configureCell:self.prototypeCell forRowAtIndexPath:indexPath];
self.prototypeCell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(self.prototypeCell.bounds));
[self.prototypeCell layoutIfNeeded];
CGSize size = [self.prototypeCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height+1;
}
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}
set number of lines for label to 0.
Add constraints for top space, bottom space, left and right space. Do not add height constraint for label.
Use below code to get better result with/without Autolayout. Need to calculate font height of label and set the position as per your requirement.
It will also helpful for calculate collectionView's dynamic cell height.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGSize constraint = CGSizeMake(screenWidth - 62, 20000.0f);
CGSize size;
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
CGSize boundingBox = [string boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:self.titleLabel.font}
context:context].size;
size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
return size.height + 16;
}

UITextView sizeThatFits returns different size than boundingRectWithSize

Need the required height of UITextView. sizeThatFits returns bigger, but the correct height than boundingRectWithSize. Why difference exist?
At two places I need to know the height. In cellForRowAtIndexPath and in heightForRowAtIndexPath.
I do not think it is efficient to create always a UITextView in heightForRowAtIndexPath just to know what height is required.
What workaround do you know to calculate height of a UITextView in heightForRowAtIndexPath?
I met similar problem last month for UITableView, and I use boundingRectWithSize to calculate the size, it is actually correct. I then put it into UITextView.
Some mistakes I made:
I forget to set the same font size when calculating and for UITextView
UITextView has margins, I will manually add it in heightForRowAtIndexPath and set textContainerInset to the same one.
Hope it helps you.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger section = indexPath.section;
NSUInteger axisIndex = section - 2;
yAxis *yAxisObj = self.yAxisInfoArray[axisIndex];
boundingRect = [yAxisObj.yAxisDescription boundingRectWithSize:CGSizeMake(self.descriptionViewWidth, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading
attributes:#{NSFontAttributeName:self.contentFont}
context:nil];
return boundingRect.size.height + TEXT_TOP_MARGIN + TEXT_BOTTOM_MARGIN;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellId = #"ChartDescriptionCell";
ChartDescriptionCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (!cell) {
cell = [[ChartDescriptionCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
cell.textView.bounces = NO;
cell.textView.showsHorizontalScrollIndicator = NO;
cell.textView.showsVerticalScrollIndicator = NO;
cell.textView.font = self.contentFont;
cell.textView.textColor = [UIColor colorWithHex:#"#333333"];
cell.textView.textContainerInset = UIEdgeInsetsMake(TEXT_TOP_MARGIN, -5, TEXT_BOTTOM_MARGIN, -5);
}
NSInteger section = indexPath.section;
NSUInteger axisIndex = section - 2;
yAxis *yAxisObj = self.yAxisInfoArray[axisIndex];
cell.textView.text = yAxisObj.yAxisDescription;
}
return cell;
}
boundingRectWithSize returns size for text, so you should manually provide your font.
sizeThatFits returns size of UITextView with this text inside
If you are pointing to iOS 8 and above you can use Dynamic cell height which is very easy. In case of iOS 7 you need some workaround.
Tutorial: http://www.raywenderlich.com/87975/dynamic-table-view-cell-height-ios-8-swift
Related question with nice answer: Using Auto Layout in UITableView for dynamic cell layouts & variable row heights

Increase row height of UITableView, Xcode

I have label in a cell, when I click on a cell I want to cell increases as much as the size of label. Currently my code resizes the cell to 240.
My label:
UILabel *cellLabel3 = (UILabel *)[cell viewWithTag:5];
[cellLabel3 setText:[Data objectAtIndex:self.expandedIndexPath.row]];
Code for cell height:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
return 240;
}
return 90.0;
}
Try this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.expandedIndexPath = indexPath;
[self.tableView reloadData];
}
This Question has answer already But still posting answer,
Calculate Height of Text from method
CGSize maximumSize = CGSizeMake(kLabelWidth, CGFLOAT_MAX);
CGSize size = [text sizeWithFont:font
constrainedToSize:maximumLabelSize
lineBreakMode:UILineBreakModeWordWrap];
And replace kLabelWidth with label width & font with your label font
and return size.height from heightForRowAtIndexPath.

iOS dynamical height of UITableViewCell and heightForRowAtIndexPath

I'm using Autolayout for my new UITableViewCells in a large project.
I've one TableView where the height of each row is calculated automatically, there I don't use the delegate function heightForRowAtIndexPath.
I've declared a estimated row height:
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
My question is: on another TableViewController there are a lot of UITableViewCells, where I programmatically need to declare the height of the cell in heightForRowAtIndexPath. I know that It would be better to convert all cell's to use a unique solution, but in this project are a lot of different cell's, so I'd like to use a workaround and combine the dynamically calculated height with autolayout and the programmatically calculated row height.
Is this possible?
If you are using iOS 8 and above, you do not need to calculate height dynamically. Auto layout will do all for you. But if you are using lower than IOS 8, you need to calculate cell height.
For IOS 8:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}
And add below code in your controller:
tableView.estimatedRowHeight = 400.0
tableView.rowHeight = UITableViewAutomaticDimension
Where estimatedRowHeight should be max height which can be for your cell.
Thanks
Calculate the height of the content dynamically using boundingRectWithSize.
If you have a UILabel which is dynamic, you can use the following :
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
/* Check Content Size and Set Height */
CGRect answerFrame = [YOUR_LABEL.text boundingRectWithSize:CGSizeMake(240.f, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) attributes:#{NSFontAttributeName:[UIFont fontWithName:#"" size:14.0f]} context:nil];
CGSize requiredSize = answerFrame.size;
return requiredSize.height;
}
You can 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;
}

How to change each UITableViewCell height dynamically?

I'm working on application where i show user comment in UILable and UILable have sizetofit property. i want to change cell height according to UILable height.
My Question is how i change cell height for example first cell height may be 50, second Cell height may be 100 and so on.
For dynamic height of UITableViewCell you have to do below things
Fulfill all constraint requirement in UITableViewCell
Tell your TableView to dynamically layout Height of every Cell with below code
- (void)viewDidLoad {
[super viewDidLoad];
// two magic lines
tableView.estimatedRowHeight = 89
tableView.rowHeight = UITableView.automaticDimension
}
With just two lines of code, you instruct the table view to calculate the cell’s size matching its content and render it dynamically. This self sizing cell feature should save you tons of code and time. You’re gonna love it.
Hope this helps you by tableview methods:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}
You can use this method for increase UITableViewCell height dynamically (No AutoLayout)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSMutableAttributedString *strName = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:#"%#",strItemName]];
[strName addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:16] range:NSMakeRange(0, strItemName.length)];
CGSize sizeItemName = CGRectIntegral([strName boundingRectWithSize:CGSizeMake(130, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil]).size;
int padding = 5;
//your default cell height for ex 55
if (sizeItemName.height < 55)
{
sizeItemName.height = 55;
}
return sizeItemName.height + padding;
}
In your heightForRowAtIndexPath, calculate the dynamic height based on the related cell data.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *data = [self.dataSource objectAtIndex:indexPath.row];
return [MyTableViewCell heightForData:data];
}
Then in your MyTabLeViewCell, write a function as below, let us say the data has the "content" which is the fact for dynamic height. And your tableViewCell defined a UILabel called contentLabel with CONTENT_LABEL_WIDTH
+(CGFloat) heightForData : (NSDictionary *)data{
self.contentLabel.text = [data objectForKey:#"content"];
CGSize contentLabelSize = [self.contentLabel sizeThatFits:CGSizeMake(CONTENT_LABEL_WIDTH, CGFLOAT_MAX)];
return contentLabelSize.height;
//If you want to have a minimum cell height no matter how small your content is, you can use below fmaxf with a pre-defined CELL_MIN_HEIGHT value.
// return fmaxf(CELL_MIN_HEIGHT, height);
}

Resources