I'm trying to get my UITableViewCell.textLabel property to autofit text both vertically and horizontally.
Right now my tableView is not scrollable, so the cell frame is not dynamic. I need the textLabel property to word wrap and resize to a set # of lines. Setting the numberOfLines to 0 isn't working because it cut off because my cell height is smaller than the textLabel final height. Using an arbitrary number for numberOfLines cuts off the text.
How can I resize my textLabel to fit within the frame of the cell?
Code below
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell* cell = (UITableViewCell*)[tableView #"mycell"];
cell.textLabel.text = #"LOTS OF TEXT";
cell.textLabel.textAlignment = NSTextAlignmentCenter;
[cell.textLabel textRectForBounds:cell.bounds limitedToNumberOfLines:0];
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
cell.textLabel.numberOfLines = 0;
cell.textLabel.adjustsFontSizeToFitWidth = YES;
cell.textLabel.minimumScaleFactor = .5;
}
EDIT: If the cells are a set height this is straightforward. You will have to set a custom cell and textLabel in it, as I don't think you can't modify the textLabel in the basic or other preset cells the way you need to. Just add a text label and set constraints linking it to the four sides, with the minimum amount (can be zero). Labels automatically center text vertically if there is extra space, so making the label too tall shouldn't be a problem.
Original answer:
If you're not using tableView self-sizing cells, maybe to support iOS 7, then it won't matter what you do in the cell because the issue is in the tableView row heights. If a single label the classic boundingRect method will work. in tableView:heightForRowAtIndexpath: do
NSString *stringFromDataSource = #"Text For Label";
CGSize potentialSize = CGSizeMake(CGRectGetWidth(self.tableview.frame), CGFLOAT_MAX);
NSStringDrawingOptions optionsForMultiline = NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading;
NSDictionary *attributes = #{
NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleBody]
}; //or whatever
CGRect rectForText = [stringFromDataSource boundingRectWithSize:potentialSize
options:optionsForMultiline
attributes:attributes
context:nil];
return CGRectGetHeight(rectForText);
Note that in your question, your various options for sizing the cell aren't going to be used if the textLabel is expanding with autolayout, unless there is a constraint capping the maximum size, at which point minimumScaleFactor and so on will kick in.
Related
I currently have a custom UITableViewCell with a number of labels in it. Not all of those labels will necessarily have a value but they could do - and they may be multiline (At least 2 labels will always be visible). If there isn't any text then the label should collapse. What I'm trying to achieve is that each label only takes up the height it needs and they all butt up to each other. I've got all of this working, except I finally want a bottom margin of 11 between the last label and the cells content view with the cell height adjusted accordingly. This is the only bit where I can't seem to get my constraints working correctly.
I correctly return the right height by creating a dummy cell, and in my cellForRowAtIndexPath method I try to set the cell and the cell's contentview frame accordingly. But the cell height that is rendered on the screen is always the same as it was defined in the xib.
I know there's a lack of code here, but all my constraints have been define in IB and each label has a trailing and leading space for with and a top space of 0 to the label above it. The top label has a top space to create a top margin to the content view, but as soon as I do this on the bottom label it stretches the height of the labels to accommodate all the constraints.
I've seen various tutorials and questions on here that relate to 2 labels where 1 might be a variable size, but I haven't been able to adapt any solutions for what I'm trying to achieve.
Okay,
Get as many labels you want (suppose you want 2 labels inside the cell,)
Create 2 labels with height 1 in storyboard, or if you are programmatically adding them, then you can do.
UILabel *myLabel1 = [[UILabel alloc]initWithFrame:[CGRectMake:(x,y,width,1)]; // assuming you know their x,y and width respectively as you need them.
Now add constraints to the labels (Be sure you make them ambiguous or else orientation would mess it up, ):
3 a. Height -(should be equalto OR greater than => 1).
b. Width - [Set it a width],Make sure it has a priority more than others but less than height
c. Pin to trailing and leading space, as well as superview top and bottom.
d. If there are two Labels make sure you click both of them and add a Vertical or horizontal space according to your needs.
I think that would make it ambiguous. (I am telling these as you wrote its about autolayout, so despite of orientation this would never get any layout problems).
Add a snippet that would calculate the height of the Label when you know the string.
CGSize constrainedSizeOfMessege = CGSizeMake(widthOfLabel, 9999);//150,180 anything you like
NSDictionary *attributesDictionaryMessege = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont fontWithName:#"System Italics" size:16.0], NSFontAttributeName,
nil];
NSMutableString *myString = [[NSMutableString alloc]initWithString:[[friendsInstanceArray objectAtIndex:indexPath.row] myString] attributes: attributesDictionaryMessege];
CGRect requiredHeightMessege = [myString boundingRectWithSize:constrainedSizeOfMessege options:NSStringDrawingUsesLineFragmentOrigin context:nil];
Add the above code in
(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
Now, calculate the height of the Cell, the height of all the labels if they are 1 height then
Return the height of the Cell in storyboard + height of the Expected Label Size.
So The method would look like.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGSize constrainedSizeOfMessege = CGSizeMake(widthOfLabel, 9999);//150,180 anything you like
NSDictionary *attributesDictionaryMessege = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont fontWithName:#"System Italics" size:16.0], NSFontAttributeName,
nil];
NSMutableString *myString = [[NSMutableString alloc]initWithString:[[friendsInstanceArray objectAtIndex:indexPath.row] myString] attributes: attributesDictionaryMessege];
CGRect requiredHeightMessege = [myString boundingRectWithSize:constrainedSizeOfMessege options:NSStringDrawingUsesLineFragmentOrigin context:nil];
int heightOfMessege = requiredHeightMessege.size.height;
//if cell height is 50 then return 50+heightOfMessege
return 50+heightOfMessege;
}
And YES POSITIVELY set, mylabel.numberOfLines = 0;
Hope this helps you out
If you want to stick labels on their position including with the empty space than give a single space in label's title text which will not make their text empty and than label will stick to positions.
I have a label in custom cell in table view.
I want to change cell height and also label height(at first I am showing only 2 line, I want to show more text) after user taped a cell,
I am able to change cell height based on cell content, but unfortunately I cannot change label height, :((
I've read tons of stackoverflow answers, but still nothing.
it is believed that this would work:
CGSize labelSize = [#"Hello World!" sizeWithFont:[UIFont systemFontOfSize:17] constrainedToSize:CGSizeMake(label.frame.size.width,MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping];
but this is deprecated in iOS 7; can any one please help me in resizing my label in cell?
If you want the height of your label relative to the height of your cell and you use Storyboard, you should determine the size of your label with constraints in relation to the ContentView of the cell. Just set the top and bottom distance from your label to the ContentView and no additional height for the label.
If you dont know how to set Storyboard and constraints, this might help: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html
UITableViewCell lays out it's content in -layoutSubviews. Therefore, if you want to simply have a custom layout logic, you need to subclass UITableViewCell and override the -layoutSubviews method. It's a good idea to call super anyway before applying your logic, though.
You may use something like this to get UIlabel size
-(CGFloat)getLabelSize:(UILabel *)label fontSize:(NSInteger)fontSize
{
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont systemFontOfSize:fontSize], NSFontAttributeName,
nil];
CGRect frame = [label.text boundingRectWithSize:CGSizeMake(270, 2000.0)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributesDictionary
context:nil];
CGSize size = frame.size;
return size.height;
}
I've done a lot of reading already on this, but it's seems like there's no simple solution.
I'm trying to make an app that loads an NSArray of NSString comments to display in a UITableView. The comments all vary in size.
How can I get the cell to adjust its size to show the entire content of each comments?
I'm trying to find a simple solution without resorting to using magic CFloat numbers.
Is there a method in apple's API that allows me to calculate the needed height of the cell.detailTextLabel given an NSString comment and fixed width?
I think if I can calculate this height, all that's need is to set the height of the cell and the height of the row.
Not really sure what order to do this in since I've read the cell hasn't been created yet when heightForRow:AtIndexPath: gets called.
Calculate the height of the text in tableView:heightForRowAtIndexPath:
You need to use boundingRectWithSize:options:context: on iOS 7+ and sizeWithFont:forWidth:lineBreakMode: on iOS 6 and below. See the apple documentation for more information.
UIFont *font = [UIFont oka_commentLabelFont];
NSString *text = [self commentForIndexPath:indexPath];
CGFloat cellWidth = 300.f;
CGSize boundingSize = CGSizeMake(widthForCell, CGFLOAT_MAX);
CGSize size;
if ([text respondsToSelector:#selector(boundingRectWithSize:options:attributes:context:)]) {
size = [text boundingRectWithSize:boundingSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{ NSFontAttributeName : font }
context:nil].size;
} else {
size = [text sizeWithFont:font
constrainedToSize:boundingSize
lineBreakMode:NSLineBreakModeWordWrap];
}
return size.height;
You need to set the font to the same font you want to use on the cell. If you put this in a performUpdates block on the tableView then you will get a nice expanding animation.
Don't set the height of the cell, set the height of the row and the table view will do the rest.
To calculate the height of the text, ask a label with textRectForBounds:limitedToNumberOfLines:, or ask the string with one of the many methods like sizeWithFont:constrainedToSize:lineBreakMode:.
You may need to deal with padding around the label when compared to the size of the cell.
yes we can calculate the height of UILabel depending on the Font and line break mode.
UIFont * font = [UIFont systemFontOfSize:12.0];
CGSize newSize = [text sizeWithAttributes:#{NSFontAttributeName:font}];// You can add other attributes in dictionary to like line break mode.
Here you got new size for the text which will be in UILabel, now in hegihtForCellAtIndexPath method you can change the size of UITableViewCell. newSize.height is height you need for label, now you can add some value to it for offsets and return it.
Use
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
I am trying to create multi-line dynamic UILabels in UITableViewCells. I have a custom UITableViewCell that has a 'comment' label. The cell and the label are created in storyboard.
I can compute the heights of the UITableViewCells properly based on the multi-line data to be stored in the UILabel (using heightForRowAtIndexPath). However, my problem lies in the actual UILabel content. The UILabel content will display only 1 line of data on table load. However, once a cell containing multiline UILabel data moves offscreen and comes back on screen, the multi-line data appears properly in the UILabel with multiple lines. Is there any way to fix this so that the multi-line data appears properly on table load?
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cCell = (CustomCell *)cell;
MyObject = [myArray objectAtIndex:indexPath.row];
cCell.commentLabel.frame = CGRectMake(65.0f, 28.0f, 243.0f, 200.0f);
cCell.commentLabel.text = MyObject.multi_line_text_data;
cCell.commentLabel.adjustsFontSizeToFitWidth = NO;
cCell.commentLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
cCell.commentLabel.font = [UIFont systemFontOfSize:13.0];
cCell.commentLabel.lineBreakMode = NSLineBreakByWordWrapping;
cCell.commentLabel.numberOfLines = 0;
[cCell.commentLabel sizeToFit];
}
Thanks!
Since you're doing this in the storyboard, you can set the necessary label properties there (lineBreakMode and number of lines). Just give the label a specific width constraint and constraints to the top, bottom, and left sides of the cell. Then, in code use sizeWithFont:constrainedToSize:lineBreakMode: in heightForRowAtIndexPath: to calculate the appropriate height for the cell based on the content of the label -- the label, because of its constraints, will expand along with the cell to the proper size. Something like this:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGSize rowSize = [self.theData[indexPath.row] sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:CGSizeMake(260, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
return rowSize.height + 30;
}
Here, 260 was the width I gave my label in IB, and the 30 is a fudge factor (determined empirically) to account for padding above and below the label.
I met the same problems. Unchecking Autolayout can fix it.
I've frustrated myself with this question for a couple of days. I'm trying to add a UILabel to a UITableViewCell. I want the UILabel to span the entire width of the cell, minus 5 or 10 on both the right and left sides for looks. My problem is in programmatically determining the size of the cell's frame in which to place the label. No matter which UITableViewStyle I use, the cell.contentVew.frame.size.width value is nowhere near the width of the cell frame itself.
For example, in the table I am constructing, I can achieve my desired result by subclassing UITableViewCell and creating a UILabel with a manually determined width (through just trial and error) by:
CGRectMake(10, 12, 397, self.contentView.frame.size.height);
But it's that 397 number that's vexing me. I want a way to programmatically determine what it should be for any width table or style. This should be a simple process by just determining the width of the entire frame of the cell and then subtracting 10 or 20 so the UILabel's edges don't actually touch the edge of the cell.
However, if I set the tableViewStyle to UITableViewStyleDefault and then try:
NSLog(#"Width: %f", self.contentView.frame.size.width);
I get 320. If I set the style to any of the other three styles, the returned number is 302. Even the 320 number isn't anywhere near the width of the cell frame (as with my manually determined number of 397).
What value do I need to access that will return the entire width of the cell's drawing frame? I'm sure, as with most vexing problems, the solution will make me want to slap myself on the forehead, but I'm to the point where I'm ready for it now.
EDIT for more info:
One clarification to anyone interested. This question of mine pertains primarily to a Grouped style table. For a plain style, the answer to my question above can be determined simply in the cellForRowAtIndexPath method by:
CGFloat cellWidth = [tableView rectForRowAtIndexPath:indexPath].size.width;
The problem I'm having is that the rectForRowAtIndexPath method returns the width of the frame in which the cell is drawn, which is fine for a plain style table since the cell width is the entire width of the frame. However, in a grouped table, the width of the cell is less than the width of the frame in which it is drawn, so this method will return a number that is quite a bit wider than the width of the cell. It's possible that the width of the cell in a grouped table style is a fixed number less than the width of the table frame, so this might be the way to solve the problem. I'll investigate that and answer my own question here if that's the case.
I have determined my own answer, and I hope it helps anyone faced with the same issue. The calculation of the margin of a grouped tableView I found on this StackOverflow answer.
This code will provide a label within a tableView cell that spans the cell with a margin between the two edges of the cell, and centered vertically within the cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UILabel *label;
CGFloat groupedStyleMarginWidth, tableViewWidth;
UIFont *labelFont = [UIFont boldSystemFontOfSize:17.0]; // Set to whatever you like
NSString *labelText = #"Test String";
// Calculate the margin between the cell frame and the tableView
// frame in a grouped table view style.
tableViewWidth = tableView.frame.size.width;
if (tableView.style == UITableViewStyleGrouped) {
if (tableViewWidth > 20)
groupedStyleMarginWidth = (tableViewWidth < 400) ? 10 : MAX(31, MIN(45, tableViewWidth*0.06));
else
groupedStyleMarginWidth = tableViewWidth - 10;
}
else
groupedStyleMarginWidth = 0.0;
if (cell == nil) {
CGRect tableViewRect;
CGRect labelRect;
CGFloat x, y, w, h, labelMargin;
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// Retrieve the rect of the table view.
tableViewRect = [tableView rectForRowAtIndexPath:indexPath];
// Set whatever margin around the label you prefer.
labelMargin = 10;
// Determine rect values for the label.
x = tableRect.origin.x + labelMargin;
// Calculate width of label
w = tableRect.size.width - (groupedStyleMargin * 2) - (labelMargin * 2);
// Calculate height of table based on font set earlier.
h = [labelText sizeWithFont:font].height;
// Calculate y position for the label text baseline to center
// vertically within the cell.
y = (tableRect.origin.y / 2) - (h / 4);
labelRect = CGRectMake(x, y, w, h);
label = [[UILabel alloc] initWithFrame:labelRect];
label.text = labelText;
label.tag = 0;
[cell.contentView addSubview:stepLabel];
[label release];
}
else {
label = (UILabel *)[cell viewWithTag:0];
}
Sounds like this would best be handled by auto layout constraints nowadays.