heightForRowAtIndexPath is never displaying multiline when the text is longer than usual.
Here is my code:
#define FONT_SIZE 14.0f
#define CELL_CONTENT_WIDTH 320.0f
#define CELL_CONTENT_MARGIN 10.0f
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat result = 44.0f;
CGFloat width = 0;
CGFloat tableViewWidth;
CGRect bounds = [UIScreen mainScreen].bounds;
if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation))
tableViewWidth = bounds.size.width;
else
tableViewWidth = bounds.size.height;
width = tableViewWidth - 110; // fudge factor, 115 isn't quite right
NSUInteger index = [indexPath row];
id doc = [[self displayedObjects] objectAtIndex:index];
NSString *title = [doc title];
if (title)
{
// The notes can be of any height
// This needs to work for both portrait and landscape orientations.
// Calls to the table view to get the current cell and the rect for the
// current row are recursive and call back this method.
CGSize textSize = { width, 20000.0f }; // width and height of text area
CGSize size = [title sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:textSize lineBreakMode:UILineBreakModeWordWrap];
size.height += 29.0f; // top and bottom margin
result = MAX(size.height, 44.0f); // at least one row
}
return result;
}
Any help will be really appreciated.
Thanks!
To make label show multiline text you must set its numberOfLines property to a maximum allowed number of lines (or to 0 for arbitrary lines number). So in cellForRowAtIndexPath: method when setup your cell add:
...
cell.textLabel.numberOfLines = 0;
...
I suppose you don't use a custom UITableViewCell. So you're probably setting the title somewhere with something similar to the following code :
cell.textLabel.text = #"long long long text";
To get your text on multiple lines you have to configure the default title UILabel to display its text on multiple lines. Like this :
cell.textLabel.numberOfLines = 3;
You can compute the number of line the same way you compute the height of the cell. In fact I think you have to compute both to do what you want (height adapted to the content).
CGSize size = [title sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:textSize lineBreakMode:UILineBreakModeWordWrap];
What matters in textSize is to set the row width for the width attribute and an height value which is "large" enough to "host" the text.
Once you have your size initialized you can divide the height by the height of the UILabel line.
Related
I'm trying to set a dynamic height for cells in my table, height should be based on a text length and a max width.
The problem appears when this text comes in a single line, without line separators. Doesn't matter how large the text is, if there are no line separators it detects that text fits in a single line so my cell height doesn't increase.
Am I doing something wrong? How can I achieve it? Thanks.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat cellheight = 35.0f; // BASE
NSString *text = #"...";
if (text) {
UIFont *font = (indexPath.row == 0) ? [UIFont systemFontOfSize:14] : [UIFont systemFontOfSize:12];
CGSize constraintSize = CGSizeMake(self.tableView.frame.size.width, CGFLOAT_MAX);
if (IS_EARLIER_THAN_IOS7) {
CGSize size = [text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:NSLineBreakByCharWrapping];
cellheight += size.height;
} else {
CGSize size = [text sizeWithAttributes:#{NSFontAttributeName: [UIFont systemFontOfSize:12.0f]}];
CGSize adjustedSize = CGSizeMake(ceilf(size.width), ceilf(size.height));
cellheight += adjustedSize.height;
}
return (indexPath.row == 0) ? cellheight + 40.0f : cellheight;
}
}
- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(NSLineBreakMode)lineBreakMode NS_DEPRECATED_IOS(2_0, 7_0, "Use -boundingRectWithSize:options:attributes:context:") __TVOS_PROHIBITED; // NSTextAlignment is not needed to determine size
You should use "boundingRectWithSize:options:attributes:context:" not "sizeWithAttributes:".
This is a sample
CGSize size = [text boundingRectWithSize:CGSizeMake(_myTableView.frame.size.width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:[UIFont systemFontOfSize:14]} context:nil].size;
There is a way more easy way to do that:
First set the text to the UILabel, and set all the required font, size. etc. Then call sizeThatFits method on the label.
CGSize sizze =[itemLabel sizeThatFits:CGSizeMake(itemNameLabelWidth, CGFLOAT_MAX)];
Also dont forget to set numberOfLines and lineBreakMode before calling sizeThatFits:
itemLabel.numberOfLines=0;
itemLabel.lineBreakMode=NSLineBreakByWordWrapping;
Note 1 : calling sizeThatFits does not set the new frame to the UILabel, it just calculates and returns the new frame. You have to then set the frame to the label by adding x and y origin values. So that becomes :
CGSize sizze =[itemLabel sizeThatFits:CGSizeMake(itemNameLabelWidth, CGFLOAT_MAX)];
CGRect namelabelFrame = itemLabel.frame;
namelabelFrame.size = sizze;
itemLabel.frame = namelabelFrame;
Note 2 : This code is okay in cellForRowAtIndexPath:, but when calculating the height inside heightForRowAtIndexPath: you may want to optimize this code a little bit. As you don't have the cell to work with you might initialize a UILabel object and perform this code on it to estimate the height. But having UIView initializations inside heightForRowAtIndexPath: is not a good idea as they can significantly affect performance while scrolling.
So what you do is have an already initialised (and all formatting applied) UILabel as a class variable and reuse that for height calculation.
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).
I'm trying to dynamicly set label size. It works in a strange way, i get some of the text cut off.
I first set my label text and then try to resize it like this way.
_switch2Label.text = #"Call on alarm, there will be no call if other user of alarm system will recieve an alarm call and confirm (answer) it by pressing 0#";
_switch2Label.numberOfLines = 0;
[self newFrame:_switch2Label];
- (void) newFrame:(UILabel *) label
{
CGSize maxSize = self.view.bounds.size;
maxSize.width = maxSize.width - 30;
CGSize labelSize = [label.text sizeWithFont:label.font constrainedToSize:maxSize lineBreakMode:label.lineBreakMode];
CGRect newFrame = label.frame;
newFrame.size.height = labelSize.height;
label.frame = newFrame;
}
I only get three lines of text, while five is needed for this label. Maybe anyone could see my mistake here? If I add more text to label it gets shown, yet still about two lines of the label text gets cutt off.
I have changed your method...Please check it..it may help you..
- (void) newFrame:(UILabel *) label
{
CGSize constraint = CGSizeMake(300, 1000.0f);
CGSize size_txt_overview1 = [label.text sizeWithFont:[UIFont fontWithName:#"Arial Rounded MT Bold" size:15] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
label.frame = CGRectMake(20,20, size_txt_overview1.width, size_txt_overview1.height+15);
}
_switch2Label.text = #"Call on alarm, there will be no call if other user of alarm system will recieve an alarm call and confirm (answer) it by pressing 0#,";
_switch2Label.numberOfLines = 0;
[self newFrame:_switch2Label];
- (void) newFrame:(UILabel *) label
{
CGSize maximumSize = CGSizeMake(label.frame.size.width, 10000);
//maxSize.width = maxSize.width - 30;
CGSize labelSize = [label.text sizeWithFont:label.font constrainedToSize:maximumSize lineBreakMode:UILineBreakModeWordWrap];
CGRect newFrame = label.frame;
newFrame.size.height = labelSize.height;
label.frame = newFrame;
}
Use this code blocks , may help u.
Why programatically resizing the label? Is this something you cannot do in IB or using autorezizeMask?
The label's constraint size isn't getting calculated as you intend, currently, your code is constraining the label height to the view's bound's height. Changing your maxSize instance to:
CGSize maxSize = CGSizeMake(self.view.bounds.size.width - 30, MAXFLOAT);
CGSize labelSize = ...
Doing so will ensure that the constraint is not bound by your view bounds. You may also want to consider setting the clipsToBounds property of your view if you want the label to be able to extend past your view's bounds.
I'm working on the project. Currently I'm facing a problem, I have 3 UITableView's in one ViewController. It looks something like this:
Now, The first TableView(top one), has just one row, that has a size of 55 pixels and it don;t have to be resizable. Next, second one is the TableView, again with one row, but this row must to resize depends on the content of it. Right now this TableView displaying this row:
This row must to resize depends on how long the text in it. What I did to calculate the size is:
if(tableView == self.usersPostOnTheWallTableView) {
NSDictionary *propertyItems = [self.repliesItems objectAtIndex:indexPath.row];
NSString *text = [self.postItems objectForKey:#"text"];
NSLog(#"%#", text);
CGSize constraintSize = CGSizeMake(280, MAXFLOAT);
CGSize sizeText = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
self.height = MAX(sizeText.height, 44.0f);
self.photosArray = [propertyItems objectForKey:#"attach_photos"];
self.videosArray = [propertyItems objectForKey:#"attach_videos"];
if((self.photosArray.count == 0) && (self.videosArray.count == 0)) {
height += (CELL_CONTENT_MARGIN * 2);
} else if ((self.videosArray.count > 0) && (self.photosArray.count == 0)) {
height += 90 + (CELL_CONTENT_MARGIN * 2);
} else if((self.photosArray.count > 0) && (self.videosArray.count == 0)) {
height += 85 + (CELL_CONTENT_MARGIN * 2);
}
}
Now, if the text is quite small, it fits the row well, and the row is resizes. Such as this:
But, after the text comes bigger, this what happening:
Obviously I don't need the scrolling right there, and I would disable it. But the thing here is that it takes the size of the text, and it resize the label, you can see how many space it make from the top and from the bottom of the label. But it don't put actual text there. Another thing is that it didn't resize the TableView it self, when I'm trying to resize it, I get the overlapping of the bottom table by the middle table.
Can anyone recommend any solution of this, or maybe another way of implementation?
Thanks in advance!
U can do this is use tableviewDelegate method for setting cell height according your text like this for example:
This method will be called for every cell to decide how much cell should have height
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *strDetails = [yourTextArray objectAtIndex:indexPath.row];
//if(indexPath.row == 1)
//{
CGSize stringSize = [strDetails sizeWithFont:[UIFont fontWithName:#"Marker Felt" size:13] constrainedToSize:CGSizeMake(320, 9999) lineBreakMode:UILineBreakModeWordWrap]; // Provide text as well as font name in which your text will be displayed and constrainedToSize which defines width and height it should be maximum
return stringSize.height+65; //Here 65 can be any value depending on Calculation ImageView height + uppermost space + bottom space added
//}
//else
//{
// return 80 // anything u wishes
//}
}
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.