How to change each UITableViewCell height dynamically? - ios

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

Related

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

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

Need to access my UITableViewCell class in UITableView's heightForRowAtIndexPath:

I need to set UITableViewCell height based on my UITextView height, but when I access cell sublcass using: CommentTableViewCell *cell = (CommentTableViewCell *)[tableView cellForRowAtIndexPath:indexPath]; in heightForRowAtIndexPath:, app crashes.
What's wrong? Isn't this the right way to access my custom cell class?
From my experience, heightForRowAtIndexPath is triggered automatically before it displays the cell. So you need to know the height of the cell before it is displayed. You can not do this in heightForRowAtIndexPath at is seems that you think that you can change it be looking at cell's text's height during heightForRowAtIndexPath.
What you need to do is create an array and store the height. You can now then use that array and extract the number inside heightForRowAtIndexPath.
Here's an example:
- (void) calculateHeight {
UILabel *tempTitle = [[UILabel alloc] init];
tempTitle.font = [UIFont fontWithName:#"your font" size:#"size"];
tempTitle.lineBreakMode = NSLineBreakByWordWrapping;
for (int i = 0; i < [yourListOfData count]; i++) {
NSString *yourText = [yourListOfData objectAtIndex:i];
CGSize textSize = [yourText
sizeWithFont:#"size"
constrainedToSize:CGSizeMake(0, "maxsize of your label that you want")
lineBreakMode:NSLineBreakByWordWrapping];
[listOfHeightPerItem addObject:[NSNumber numberWithFloat:(textSize.height + 20)]];
//+20 for padding
}
}
and then in your heightForRowAtIndexPath:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat tempHeight = [(NSNumber *)[listOfHeightPerItem objectAtIndex:indexPath.row] floatValue];
return tempHeight;
}
Please note that this isn't the most optimal way but it works... especially when your deadline was yesterday ;)
If the UITextView height depends on some string length unknown until run time, then you should follow MVC architecture and determine the height from that string as defined in some model object. You can use NSString sizeWithFont or sizeWithAttributes methods to get the text size.

Resize UILabell in iOS 7.1 in Custom Cell

I have a table view with custom cells. I'm using Xcode 5.1 and iOS 7.1. I would like to adjust a UILabel height based on the length of a string and also dynamically adjust the height of the cell to fit the label.
In
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
cell.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
cell.titleLabel.numberOfLines = 0;
[cell.titleLabel sizeToFit];
Some seem to resize correctly, others don't. As for the height of the cell, I can't get it to update based on the height of the label.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [self dynamicHeightAtIndexPath:indexPath]+20;
}
-(CGFloat)dynamicHeightAtIndexPath:(NSIndexPath *)indexPath
{
CGSize maximumSize = CGSizeMake(275, 9999);
UIFont *myFont = [UIFont fontWithName:#"MyriadPro" size:11];
CGSize stringsize = [[self.array objectAtIndex:indexPath.row] sizeWithFont:myFont
constrainedToSize:maximumSize
lineBreakMode:NSLineBreakByWordWrapping];
return stringsize.height;
}
The cell's height is determined solely by the value you return from tableView:heightForRowAtIndexPath:. This is a <UITableViewDelegate> method.
If you want to have the cell height be dynamic, you would need to perform a calculation in tableView:heightForRowAtIndexPath: based on the particular string that appears at that index path.
If you plan on doing a lot of scrolling, this calculation could get expensive and cause performance issues. If that is the case, I'd recommend caching the computed heights or computing them all at once and storing them in an array parallel with your data source.

How to resize UITableViewCell to fit its content?

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.

Resources