Changing height of UITableViewCell Dynamically - objective-c

I am trying to show comments of each User in my TableView. The comments can BE accommodated by an Image.
Usually We can set height of a UITableViewCell as heightForRowAtIndex. However I want each Cell to Expand according to the Chat And if Image included upto image height.
How can I increase the height of UITablViewCell as per its contents?

You're still going to have to do it in heightForRowAtIndex:, except that you'll have to dynamically determine the height.
I'm envisioning something like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CommentModel *comment = self.comments[indexPath.row];
CGFloat height = [CommentTableViewCell heightForComment:comment];
return height;
}
And then in your CommentTableViewCell, you have a class method that goes something like:
// Calculates the height a CommentTableViewCell should be when configured with the given comment
+ (CGFloat)heightForComment:(CommentModel *)comment {
// This part of the code will be very specific to your own project
CGFloat height = BASE_HEIGHT;
height += comment.image.size.height;
height += comment.message.size.height;
return height;
}
I know that this code probably doesn't match your code base, but it should give you a general idea of one way your goal can be accomplished.

USE THIS CODE IF YOU DONT HAVE CUSTOM CELL CLASS.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
float PADDING = 0.5f; // USE FOR DIFFERNCE BETWEEN YOUR ENDING TEXT AND SEPERATOR
UIImage *image =[UIImage imageNamed:#"test.png"]; // Some Image you want to put dynamically.
NSString *text = #"Sometext That you want tro fill Dynamically";
CGSize textSize = [text sizeWithFont:[UIFont boldSystemFontOfSize:14.0f] constrainedToSize:CGSizeMake(270 , 1000)];
return ((textSize.height+image.size.height+ PADDING * 3) + 30);
}
IF YOU HAVE CUSTOM CLASS THEN YOU HAVE TO PASS THE HIEGHT TO CUSTOM CELL TO MANAGE YOU LABEL HIEGHT SAME AS YOU EXPAND YOUR CELL OVERTHERE. YOU CAN DO IT SEPARATELY IN CELL CLASS BUT TO SYNCHRONIZE IT WITH YOUR ACTUAL HIEGHT F CELL. YOU HAVE TO PASS HIEGHT IN CUSTOM CLASS WHEN YOU WILL SETTING ITS VALUES.

Related

Return size of the cell for "cellForRowAtIndexPath" function?

The cells for my table view can hold a maximum 140 characters, So for some cells in my UITableView the height will need to be slightly increased. I'm not looking for anything fancy, 140 characters would require the cell to be increased about twice its default height of 60.
I saw this stack overflow post:
Using Auto Layout in UITableView for dynamic cell layouts & variable row heights
and downloaded the iOS 7 sample project only to find 50+ unique functions which dynamically set the cell heights. Is this really necessary for the rare occasion of 140 character messages?
Can't I simply set set the cell height within this very function?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"chatCell" forIndexPath:indexPath];
// Configure the cell...
NSDictionary *message = self.messages[indexPath.row];
UILabel *lblUsername=(UILabel *)[cell viewWithTag:1];
UILabel *lblBody=(UILabel *)[cell viewWithTag:2];
lblUsername.text = [message valueForKeyPath:#"author"];
lblBody.text = [message valueForKeyPath:#"body"];
return cell;
}
I only need to implement an if statement like this:
if (lblBody.text.length <= 25) {
// there's little text, keep the default height
} else if (lblBody.text.length <= 50) {
// make the height of this cell slightly bigger
} else if (lblBody.text.length <= 75) {
// make the height of this cell moderately bigger
} else {
// make the height of this cell large
}
//etc...
return cell;
And thus the work for this part finished. Is this possible?
You can set the row height in heightForRowAtIndexPath. Retrieve the text for that index path from your messages array and calculate the height. The code below resizes the height according to the label text.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat height = 0.0f;
NSDictionary *message = self.messages[indexPath.row];
NSString *text = [message valueForKeyPath:#"body"];
CGSize constraint = CGSizeMake(self.frame.size.width, MAXFLOAT);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping];
// MIN_CELL_HEIGHT in case you want a default height
height = MAX(size.height, MIN_CELL_HEIGHT);
return height;
}

How to make UITextView in section header adjust its height to its content

I cannot get this to work. I am using autolayout on the current view controller. I have a UITableView that has section headers and each section header has UITextView that has text that varies in length depending on the section. I cannot make it enlarge its height automatically to fit the contents so there will be no need for scroll (its contents are attributed text)
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
//UITextView *tv = [[UITextView alloc] init];
//tv.editable = NO;
//tv.attributedText = [self millionaireResults][section][#"header"];
//return tv;
return [self millionaireResults][section][#"headerview"]; //this is a uitextview
}
// this did not workeither
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
UITextView *tv = [self millionaireResults][section][#"headerview"];
return tv.frame.size.height;
}
How can this problem be solved?
I updated the code per the suggestion of Michael below
Make your "UITextView *tv" object a property and then you can do something like this (assuming you only have exactly one section to your table view):
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return (self.tv.frame.size.height);
}
If you have more sections (which is appears you do), you should make that property a NSArray of UITextView objects.
This also means you need to set the contents of your "tv" object before "viewForHeaderInSection:" gets called.
This is the answer that worked for me
When you are creating the UITextView, you must set the scrollEnabled
to false.
Your UITextView must be given the width that covers horizontal space otherwise auto size calculation are off (sometimes it is sometimes it is not, i think depending on wordbreak or something, but it was inconsistent!) and only fixes itself if you rotate the device to force redraw
In the heightForHeaderInSection method, you must get the
sizeThatFits and return its height as the height of your text view
Here is the height calculation (I found this on this site http://www.raywenderlich.com/50151/text-kit-tutorial )
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
UITextView *tv1 = (UITextView *)[self millionaireResults][section][#"headerview"];
// sizethatfits calculates the perfect size for this UITextView
// if you gave your UITextView full width
CGSize goodsize = [tv1 sizeThatFits:tv1.frame.size];
return goodsize.height+4; // here 4 is not necessary, i just put it as an offset
}
Here is the code that creates those UITextView objects
for (int i = 0; i < [milarr count]; i++) {
UITextView *tv = [[UITextView alloc] init];
tv.editable = NO;
tv.attributedText = milarr[i];
// labelTopTitle and this table in question have same width in an autoLayouted view
// so i am giving width of labelTopTitle to let UITextView cover full available
// horizontal space
tv.frame = CGRectMake(0, 0, self.labelTopTitle.frame.size.width,FLT_MAX);
//tv.backgroundColor = [UIColor grayColor];
//tv.textContainerInset = UIEdgeInsetsZero;
tv.scrollEnabled = NO;
[results addObject:#{#"headerview": tv,
#"rows":#[...]
}
];
}

Determining the width/height of a UILabel within a UITableViewCell with an image

I’m running into a problem with determining the height of a table cell, when the table cell has a lot of text and an image. But the problem is context sensitive and depends on the type of text used. I’ll explain the issue, then give the code I’m using to format the cell and to determine cell height.
When I have an image to the left of the cell, I’m taking the cell height from the maximum height of the image versus the text. The problem is that the text height is not being computed correctly in all circumstances. Below, I have the following text in the bottom cell:
“P 154 Vietnamese home cooking: very good!! Blenderized the spices into a paste-- worked well; used remaining sauce on zuchinni to grill; worked well; thick pork chops; cooked at between 450 and 525 for 35 minutes”.
The table view is scrolled all the way to the bottom. As you can see the bottom portion of the text is not present (“cooked at between 450 and 525 for 35 minutes”). What is actually going on is that the text width is not being determined correctly. Below, the text width is being computed as cell.textLabel.bounds.size.width: 234 pixels. Based on this, the height of the text is incorrectly computed.
However, if I press on the accessory button (“>”) and descend into my next view, then return from that view (using a “back” button with a navigation controller), I typically get the following changed table display (but sometimes I still get the view above without the remaining text):
In this case, the second time the table view is displayed, the width of the text is computed as cell.textLabel.bounds.size.width: 163 pixels.
The difference between the two text widths (234 - 163 = 71 pixels). The image width is 100 pixels.
The situation is, unfortunately, even more complicated than this:
1) If I have no image in the cell, I have no problem with determining the width of the text.
2) If I use just a series of simple lines of text such as a consecutive series of numbers/letters with a carriage return after each character, the width is correctly determined. For example, if the text in the cell is:
1
2
3
4
5
6
7
8
9
0
A
B
C
3) If there are cells following (below) the cell with the large amount of full-line text (not the text consisting of characters followed by carriage returns), then I never get the cell width computed correctly (and hence the height is never computed correctly).
What Have I Tried?
Within cellForRowAtIndexPath, I have tried:
cell.textLabel.autoresizingMask = UIViewAutoresizingNone;
as advised by Dynamic UILabel Heights/Widths in UITableViewCell in all Orientations but this didn’t change the situation.
I also tried putting in the suggestion from Calculating multiline text height for UILabel/UITableViewCell: different results when calculating vs actual drawing into cellForRowAtIndexPath that forces a new width for the text label, but this didn’t work.
A Hack That Worked
What did work was to force the text label width to be the correct width within heightForRowAtIndexPath. However, this is certainly a hack. I’d like a better solution. Ideas?
Code
In the code that follows BUG74 is the issue I've been describing here.
// https://stackoverflow.com/questions/129502/how-do-i-wrap-text-in-a-uitableviewcell-without-a-custom-cell
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomCellCommentList";
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
NSLog(#"CommentList.cellForRowAtIndexPath: nil case");
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
IF_IOS6_OR_GREATER(
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
);
IF_LESS_THAN_IOS6(
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
);
// To remove any maximum limit, and use as many lines as needed, set the value of this property to 0.
cell.textLabel.numberOfLines = 0;
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:17.0];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
cell.textLabel.autoresizingMask = UIViewAutoresizingNone;
}
NSDictionary *menuItemCommentDictionary =
[Globals GetIthMenuItemComment: indexPath.row forMenuItem: menuItemName andRestaurant: restaurantName];
//NSDate *commentDate = [menuItemCommentDictionary objectForKey:#"date"];
if (CommentListDebug) NSLog(#"cellForRowAtIndexPath");
UIImage *theIcon;
NSString *imageFileName = [menuItemCommentDictionary objectForKey:COMMENT_KEY_IMAGE_FILENAME];
if (imageFileName) {
// Scaling images in table view cells
// https://stackoverflow.com/questions/9046573/uitableviewcell-resize-image
#define DEFAULT_TABLE_CELL_HEIGHT 44
#define IMAGE_WIDTH_IN_TABLE_CELL 100
/* I'm going to give images a constant width and adjust
the height of the table cell according to their height.
*/
theIcon = [PersistentStorage retrieveIconFromFile:imageFileName];
if (CommentListDebug) NSLog(#"icon: %#", theIcon);
//NSNumber *iconHeight = [menuItemCommentDictionary objectForKey:#"imageIconHeight"];
//if (CommentListDebug) NSLog(#"iconHeight: %d", [iconHeight intValue]);
cell.imageView.image = theIcon;
} else {
if (CommentListDebug) NSLog(#"No image file name");
cell.imageView.image = nil; // Otherwise, if using old cell, uses old icon image
}
NSString *cellText = [menuItemCommentDictionary objectForKey:#"menuItemComment"];
// 1/5/13; Bug# 62; only if there is no icon/image and no text
// should we mark as empty.
if ((! imageFileName) && ((nil == cellText) || ([cellText length] == 0))) {
cellText = EMPTY_TEXT; // temporary empty text
}
cell.textLabel.text = cellText;
// BUG74: 1/29/13;
// We are not having problems with the text width when there
// is no icon, so only do this when there is an icon
// NOTE: This does *not* work; for some reason, the frame.size.width
// is not retained in the call to heightForRowAtIndexPath.
if (imageFileName) {
CGRect labelFrame = cell.textLabel.frame;
labelFrame.size.width = 287 - 27 - theIcon.size.width;
NSLog(#"CommentList.cellForRowAtIndexPath: labelFrame.size.width: %d", (int) labelFrame.size.width);
cell.textLabel.frame = labelFrame;
[cell.textLabel sizeToFit];
}
return cell;
}
- (CGFloat)tableView:(UITableView *)thisTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *menuItemCommentDictionary =
[Globals GetIthMenuItemComment: indexPath.row forMenuItem: menuItemName andRestaurant: restaurantName];
NSString *cellText = [menuItemCommentDictionary objectForKey:#"menuItemComment"];
// 1/5/13; Bug# 62; I'm going to allow empty comments in the case
// where there is no picture and no text. This is to allow a user
// to have a smiley rating only but no picture and no text.
// The issue here is that with no text, the height of the row
// is too small, and looks odd. And it's hard to select that row to
// delete because it's not very tall.
// My fix for this it to add some temporary empty text. I've used
// non-white space text, because I still get the issue if I use
// white space. If I add the display of smiley's in the comment
// list then this issue should go away).
// For some reason the cellText does not seem to be nil when there
// is no text.
if ((nil == cellText) || ([cellText length] == 0)) {
cellText = EMPTY_TEXT; // temporary empty text
}
//NSDate *commentDate = [menuItemCommentDictionary objectForKey:#"date"];
NSString *imageFileName = [menuItemCommentDictionary objectForKey:COMMENT_KEY_IMAGE_FILENAME];
// If I directly retrieve the icon from the file and display it
// that way, I get a mess! The image is much too large!!
UIImage *theIcon = nil;
if (imageFileName) {
theIcon = [PersistentStorage retrieveIconFromFile:imageFileName];
}
UIFont *cellFont = [UIFont fontWithName:#"Helvetica" size:17.0];
// Some of the following code modified from:
//https://stackoverflow.com/questions/1947970/dynamic-calculation-of-uilabel-width-in-uitableviewcell
// Find the cell for this index path
UITableViewCell *cell = [self tableView:thisTableView cellForRowAtIndexPath:indexPath];
//CGFloat cellHeight = cell.frame.size.height;
// Calculate text size after forcing a layout
[cell layoutIfNeeded];
//CGSize textSize = [cell.textLabel.text sizeWithFont:cellFont constrainedToSize:CGSizeMake(cell.contentView.bounds.size.width, MAXFLOAT) lineBreakMode:cell.textLabel.lineBreakMode];
NSLog(#"CommentList.heightForRowAtIndexPath: cell.textLabel.bounds.size.width: %d", (int) cell.textLabel.bounds.size.width);
// In my tests, bounds.size.width is the same as frame.size.width
NSLog(#"CommentList.heightForRowAtIndexPath: cell.textLabel.frame.size.width: %d", (int) cell.textLabel.frame.size.width);
// cell.contentView.bounds.size.width gives the full width of the
// cell contents, which was 287 pixels when I tested it
if (CommentListDebug) NSLog(#"CommentList.heightForRowAtIndexPath: cell.contentView.bounds.size.width: %d", (int) cell.contentView.bounds.size.width);
// parameters to CGSizeMake are: width, height
// the width parameter here is supposed to be the text width;
CGSize constraintSize;
if (theIcon) {
// Start hack for BUG74; 1/29/13
// The number 27 just works; Is it the width of the
// accessory button within the cell?
// cell.contentView.bounds.size.width was 287 when I checked.
int textWidth = cell.contentView.bounds.size.width - 27 - theIcon.size.width;
constraintSize = CGSizeMake(textWidth, MAXFLOAT);
// End hack for BUG74; 1/29/13
} else {
constraintSize = CGSizeMake(cell.textLabel.bounds.size.width, MAXFLOAT);
}
CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:cell.textLabel.lineBreakMode];
//NSNumber *iconHeight = [menuItemCommentDictionary objectForKey:#"imageIconHeight"];
#define LABEL_HEIGHT_EXTRA 20
#define ICON_HEIGHT_EXTRA 10
int textHeight = labelSize.height + LABEL_HEIGHT_EXTRA;
if (theIcon) {
//if ([iconHeight intValue] > 0) {
// Add 10 EXTRA here to give some white space between icons;
// with no white space, it can be hard to tell where one icon
// starts and where another ends, particularly because
// icons have variable height.
int iconHeight = ((int) theIcon.size.height) + ICON_HEIGHT_EXTRA;
/* if (CommentListDebug) */NSLog(#"CommentList.heightForRowAtIndexPath: icon height: %d; icon width: %d", (int) theIcon.size.height, (int) theIcon.size.width);
// BUG74: 1/28/13; If the text height is taller than the image
// height use the text height as the cell height.
NSLog(#"CommentList.heightForRowAtIndexPath: iconHeight= %d, textHeight= %d", iconHeight, textHeight);
if (iconHeight >= textHeight) return iconHeight;
return textHeight;
//return [iconHeight intValue] + 10;
} else {
// No image; use label size for height.
return textHeight;
}
}
So please correct me if I am mistaken but if you were able to increase the size of the cell (like the bottom picture) then everything would work how you wanted it to? If that is the case you can override:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
This would allow you to specify how tall you want the cells. This way you could set them and prevent them from cutting of the text on the bottom (like the first picture).

Dynamic table cell height with Autolayout iOS 6 +

I have UITableviewCell subclass. In that cell, I have 2 labels (lblComment and lblDateTimeStampe) and one view to show rating stars.
I want dynamic height of lblComment to fit all the text. It should expand & shrink in height depending on the length of comment.
I have implemented this before but WITHOUT AutoLayout like below
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *label = self.userComment.commentText;
CGSize stringSize = [label sizeWithFont:[UIFont boldSystemFontOfSize:15]
constrainedToSize:CGSizeMake(320, 9999)
lineBreakMode:UILineBreakModeWordWrap];
return stringSize.height+10;
}
Now I am using AutoLayout feature.
How can I achieve this using Autolayout?
Any kind of help is appreciated. Thanks
Unfortunately Auto Layout won't help you with tableView:heightForRowAtIndexPath. You still have to implement that method.
You can use UIView's systemLayoutSizeFittingSize: method but that means you'd have to instantiate and configure a table view cell which can be quite costly. You could keep one off-screen though and reuse it for calculations. But at this point, you don't really save much in terms of development effort, so doing the calculations as you did before manually is probably the best/fastest way to do this.
You can use the freely available Sensible TableView framework. The framework automatically resizes the cells as their content grows. It also does that dynamically if the table view is already displayed.
I have implemented a solution to the same problem with using autolayout and it works.
First, you need to define heightConstraint for lblComment.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UIFont *font = [UIFont fontWithName:#"YourFontName" size:YourFontSize] ;
UITextView *calculationView = [[UITextView alloc] init];
[calculationView setFont:font];
[calculationView setTextAlignment:NSTextAlignmentLeft];
[calculationView setText:lblComment.text];
int width = 0;
if(self.appDelegate.isDeviceiPhone)
width = 284;
else
width = 720;
CGSize size = [calculationView sizeThatFits:CGSizeMake(width, FLT_MAX)];
return size.height;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//initialize the cell..
UIFont *font = [UIFont fontWithName:#"YourFontName" size:YourFontSize];
UITextView *calculationView = [[UITextView alloc] init];
[calculationView setFont:font];
[calculationView setTextAlignment:NSTextAlignmentLeft];
[calculationView setText:cell.lblComment.text];
int width = 0;
if(self.appDelegate.isDeviceiPhone)
width = 284;
else
width = 720;
CGSize size = [calculationView sizeThatFits:CGSizeMake(width, FLT_MAX)];
cell.lblDetailHeightConstraint.constant = size.height;
// the other stuff...
}
Hope this helps.
If you setup the constraints correctly in IB this should work. You should not have to add the elements programmatically although as you said that will work as well.
Assuming that you have label1(variable height), label2, and view1 in the tableviewcell in that order you should:
Set fixed height constraints on label2 and view1
Pin the bottom of view1 to the bottom of the cell
Pin the vertical spacing of view1 and label2
Pin the vertical spacing of label2 and label1
Pin the top spacing of label1 to the top of the cell
Just make sure that you do NOT have a height constraint on label1, if you do it should only be greater than or equal to. With a configuration like this, you can continue using heightForRowAtIndexPath and label1 will expand and contract vertically based on the height of the cell.

How to make UITableView row as wide as the label in it dynamically?

I have a UITableView where each row may have a label anywhere from no lines to any number of lines (realistically 3-4). I am wanting the row to be expanded based on the UILabelView contents.
I have this so far:
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat height = 60.0;
UIFont *font = [UIFont fontWithName:#"Helvetica-BoldOblique" size:10.0];
NSString *text = reminder.notes; //the text that will be in the UiLabelView
CGFloat notesHeight = [text sizeWithFont:font
constrainedToSize:CGSizeMake(250.0, 4000.0)
lineBreakMode:UILineBreakModeTailTruncation].height;
height += notesHeight;
return height;
}
I still get screwy results. What can I do to accomplish my goal?
Thank you
you are using UILineBreakModeTailTruncation as lineBreakMode which will always result in a single line and sizeWithFont:constrainedToSize:lineBreakMode: will then return the needed size of that single line.
Try UILineBreakModeWordWrap instead.
edit: I hope you have set the numberOfLines-property of a UILabel to 0 (zero) so that your label is able to draw many lines.