NSString sizeWithFont:constrainedToSize: computing wrong height - ios

I have a Subtitle style UITableViewCell which height changes dynamically depending on the length of the text for each field. The problem is that the textLabel's height (CGSize size) does not increase if the label has multiple lines.
The weird part is that the detailTextLabel's height is increasing as it should (CGSize size2). The code to calculate both heights are identical.
Here is my function:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
SETLISTFMNS0Song *song = [[[selectedSetlist.sets objectAtIndex:indexPath.section] songs]objectAtIndex:indexPath.row];
CGSize size = [song.name sizeWithFont:[UIFont fontWithName:setlistFont size:labelFontSize] constrainedToSize:CGSizeMake(self.setsTable.bounds.size.width, CGFLOAT_MAX)];
NSLog(#"Label: \"%#\" \tLabel Size: %f W %f H", song.name, size.width, size.height);
NSMutableString *detail;
if ([song cover]) {
detail = [[NSMutableString alloc] initWithFormat:#"(%# cover)", [[song cover] name]];
}
if ([song with]) {
if (!detail) {
detail = [[NSMutableString alloc] initWithFormat:#"(with %#)", [[song with] name]];
}
else {
[detail appendFormat:#" (with %#)", [[song with] name]];
}
}
if ([song info]) {
if (!detail) {
detail = [[NSMutableString alloc] initWithFormat:#"(%#)", [song info]];
}
else {
[detail appendFormat:#" (%#)", [song info]];
}
}
if (detail.length != 0) {
CGSize size2 = [detail sizeWithFont:[UIFont fontWithName:setlistFont size:detailFontSize] constrainedToSize:CGSizeMake(self.setsTable.bounds.size.width, CGFLOAT_MAX)];
size.height += size2.height;
NSLog(#"Detail Label: \"%#\" \tDetail Label Size: %f W %f H", detail, size2.width, size2.height);
}
return size.height + 5;
}
I am also setting both textLabel's numberOfLines property to 0 in cellForRowAtIndexPath to support multiple lines:
cell.textLabel.numberOfLines = 0;
cell.detailTextLabel.numberOfLines = 0;
UPDATE: Thanks to #josh I now understand why this is happening. I had the width constraints set to the width of the UITableView, which is too wide. Anyone know how to find the width of the UILabel before it is created? HA!
Thanks!

How long are the strings you're calculating the size against? I ask because you're sizeWithFont:constrainedToSize: function is constraining to the full width of the cell, but your labels are probably not the full width of the cell.
What I suspect is happening is song.info is just short enough that it would fit on one line if the line were the full width of the cell, and that your song detail is long enough that it is calculating the correct number of lines, but not so long as to exceed the calculated height.
All of that to say, I think what you need to do is find out the widths of textLabel and detailTextLabel and set your constraints to those values.
Update - A way of calculating label widths
Since the width of the labels inside of a cell are dependent on the width of the cell, and since cell's aren't created at the time heightForRowAtIndexPath: is called, we need to come up with a way to know the labels' widths before they the widths are set. The only way to do this is to set the widths ourselves. Here's how I would do it:
MyCell.h
#import <UIKit/UIKit.h>
#interface MyCell : UITableViewCell
+ (CGFloat)textLabelWidthForCellOfWidth:(CGFloat)cellWidth;
#end
MyCell.m
#import "MyCell.h"
#implementation MyCell
- (void)layoutSubviews {
[super layoutSubviews];
CGRect frame = self.textLabel.frame;
frame.size.width = [MyCell textLabelWidthForCellOfWidth:self.frame.size.width];
self.textLabel.frame = frame;
}
+ (CGFloat)textLabelWidthForCellOfWidth:(CGFloat)cellWidth {
// This calculation can be as complex as necessary to account for all elements that affect the label
return cellWidth - 20;
}
#end
Then in your heightForRowAtIndexPath: implementation you can call the same class method:
CGFloat labelWidth = [MyCell textLabelWidthForCellOfWidth:self.tableView.frame.size.width]; // Since non-grouped cells are the full width of the tableView
CGSize size2 = [detail sizeWithFont:[UIFont fontWithName:setlistFont size:detailFontSize] constrainedToSize:CGSizeMake(labelWidth, CGFLOAT_MAX)];
You would create a separate Class Method (+) for each label that you need to reference.

as looking at your code i found you are missing to define "lineBreakMode".
CGSize theSize = [song.name sizeWithFont:[UIFont fontWithName:setlistFont size:labelFontSize] constrainedToSize:CGSizeMake(self.setsTable.bounds.size.width, CGFLOAT_MAX) lineBreakMode:UILineBreakModeWordWrap];
update your code with this. i hope it will fix your problem. Good luck

Related

Customized UITableViewCell Update Subviews

I have a customized a UITableviewCell. There is a title label and a detail label inside.
Now I want to adjust the detail label attributes according to the content.
If the string size is greater than the frame then set the number of line to 2.
I have tried to put the code in the cellForRowAtIndexPath or layoutSubViews in the cell class.
The piece of code is like
TransportationViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
UIFont* font = cell.detailLabel.font;
NSDictionary* attribute = #{NSFontAttributeName:font};
const CGSize textSize = [cell.detailLabel.text sizeWithAttributes: attribute];
if (textSize.width > cell.detailTextLabel.frame.size.width && cell.detailLabel.numberOfLines == 1) {
NSLog(#"%lf, %lf, %lu", cell.detailLabel.frame.size.width, textSize.width, (long)cell.detailLabel.numberOfLines);
cell.detailTextLabel.font = [UIFont systemFontOfSize:8];
cell.detailTextLabel.numberOfLines = 2;
[cell setNeedsLayout];
}
It actually passed the if condition but the setting of label doesn't work.
write below code in view didload
self.theTableView.estimatedRowHeight = 100;
self.theTableView.rowHeight = UITableViewAutomaticDimension;
[self.theTableView setNeedsLayout];
[self.theTableView layoutIfNeeded];
In cellForRowAtIndexpath cell.detailTextLabel.numberOfLines = 0;
set numberOfLines = 0;
this will solve your problem.
Edit 1: code for calculating dynamic height.
CGSize boundingBox = [label.text boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:label.font}
context:context].size;
Thne on the basis of this height you can do the further calculations.
For more info, you can check this answer https://stackoverflow.com/a/27374760/5660422

How to change the height of a custom cell individually within a UITableView?

I am trying to change the height of each cell individually based on how long my UILabel is, but it does not seem to change.
I have found the following question: How to change cell height dynamically in UITableView static cell
I tried the solution from that question:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//return 110;
NewsFlashCustomCell *cell;
static NSString *CellIdentifier = #"CustomCell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
[cell layoutIfNeeded];
return cell.messageLabel.frame.origin.y + cell.messageLabel.frame.size.height;
}
However, that seems to do nothing.
Here's a screenshot:
I'd like the height of the cells containing "tidal wave" and "mashup" to be smaller than the last 2 cells since it has a longer message.
I cannot perform a indexPath.row check to change each cell individually because each message is pulled from a database, so the length of the message varies.
What can be done?
In case it helps, here's a screenshot of my constraints:
Thanks.
UPDATE:
I added the following code in viewDidLoad:
tableView.rowHeight = UITableViewAutomaticDimension;
This is what it looks like:
If you can't set try with other method reduce the label font size depend upon the cell height .
label.numberOfLines=0;
label.clipsToBounds=YES;
label.adjustsFontSizeToFitWidth=YES;
label.lineBreakMode = NSLineBreakByWordWrapping;
You can calculate height for label from its length of text and do it like following way:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return [self calculateHeightForCellAtIndexPath:indexPath];
}
#pragma mark - cell height handling
-(CGFloat)calculateHeightForCellAtIndexPath:(NSIndexPath *)indexP{
UILabel *lbl = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width-22, defaultHeightOfYourLabel)];
NSString *strLbl;
strLbl = [yourArray objectAtIndex:indexP.row];
lbl.text = strLbl;
if (IS_IPAD) {
[lbl setFont:fontRegular15];
}else{
[lbl setFont:fontRegular12];
}
UIFontDescriptor * fontDLBLTime = [lbl.font.fontDescriptor
fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold];
if (IS_IPAD) {
[lbl setFont:[UIFont fontWithDescriptor:fontDLBLTime size:15]];
}else{
[lbl setFont:[UIFont fontWithDescriptor:fontDLBLTime size:12]];
}
[lbl setLineBreakMode:NSLineBreakByClipping];
[lbl setNumberOfLines:0];
int noOfLines = [GlobalFunction lineCountForLabel:lbl];
CGFloat height = [GlobalFunction heightForWidth:lbl.frame.size.width usingFont:lbl.font forLabel:lbl];
if (noOfLines>1) {
return height + 33; //here 33 is sum of top and bottom space from view.
}
return 50; //default height of cell.
}
In your class implement following methods:
#define CGFLOAT_MAX_HEIGHT 2000.0
+ (int)lineCountForLabel:(UILabel *)label {
return ceil([self heightForWidth:label.frame.size.width usingFont:label.font forLabel:label] / label.font.lineHeight);
}
+(CGFloat)heightForWidth:(CGFloat)width usingFont:(UIFont *)font forLabel:(UILabel *)lbl{
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
CGSize labelSize = (CGSize){width, CGFLOAT_MAX_HEIGHT};
CGRect r = [lbl.text boundingRectWithSize:labelSize options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName: font} context:context];
return r.size.height;
}
You can customize lbl on basis of your font and label size.
Have your label constrain the bounds of your cell, like your already doing. Be sure to include padding around it so it looks nicer.
In your heightForRowAtIndexPath: calculate the height required for your label (it is important you have your label correctly configured for multiline and/or word wrapping)
Calculate the required height of label
CGRect frame = [label.text boundingRectWithSize:CGSizeMake(cell.width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName: font} context:nil];
Return the calculated height plus padding
frame.size.height + TopPadding + Bottom Padding
This is a general approach to have a dynamically sizing cell.
Note: Sometimes this is off by a few points so you may need to add up 5 extra points in the cell height to get all the text fitting in the label.
Please write following line in viewDidLoad:
_tableView.rowHeight = UITableViewAutomaticDimension;
Please make sure of following points:
Dont create height constraint of label
Make width of that label equal to screen size or content view
Apply leading, trailing, top and bottom constraint
Dont write heightForRowAtIndexPath method
Please let me know the results
EDIT :
Write following line in your cellForRowAtIndexPath method
myLabel.preferredMaxLayoutWidth = cell.contentView.frame.size.width;

Calculate Cell height on basis of label text + image

I have created a custom cell that have IMAGE view, and two label's the data of labels's are populated from a plist file, the data is populated properly but on front end the cell didn't show the data properly, the label cut's the data. I am using Uilabel view's.
Please have a view to my code, i have search over internet and followed some tutorial's as well but nothing work's.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Customviewcell *cell=[tableView dequeueReusableCellWithIdentifier:#"cell"];
UIImage * image = [UIImage imageNamed:justThumbs[indexPath.row]];
cell.CustomTitle.text=justTitles[indexPath.row];
cell.CustomTitle.numberOfLines =0;
[cell.CustomTitle sizeToFit];
cell.CustomDes.text=justDesc[indexPath.row];
cell.CustomDes.numberOfLines=0;
[cell.CustomDes sizeToFit];
[cell.CustomTitle layoutIfNeeded];
[cell.CustomDes layoutIfNeeded];
[cell layoutIfNeeded];
cell.Customimage.image=image;
return cell;
}
Code for calculating the height as per stackoverflow different question's answer's.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Calculate Height Based on a cell
if (!self.customcell) {
self.customcell=[tableView dequeueReusableCellWithIdentifier:#"cell"];
}
// Configure Cell
UIImage * image = [UIImage imageNamed:justThumbs[indexPath.row]];
self.customcell.CustomTitle.text=justTitles[indexPath.row];
self.customcell.CustomTitle.numberOfLines=0;
[self.customcell.CustomTitle sizeToFit];
self.customcell.CustomDes.text=justDesc[indexPath.row];
self.customcell.CustomDes.numberOfLines=0;
[self.customcell.CustomDes sizeToFit];
self.customcell.Customimage.image=image;
//Layout Cell
//Get Hieght for the cell
if([[UIDevice currentDevice]userInterfaceIdiom]==UIUserInterfaceIdiomPhone)
{
if ([[UIScreen mainScreen] bounds].size.height == 568)
{
CGRect frame = [NSString setAttributeWithString:self.customcell.CustomTitle.text withLineSpacing:0.2 withSize:CGSizeMake(270, 999999999) withFont:self.customcell.CustomTitle.font withLabel:self.customcell.CustomTitle setLabelTextColor:self.customcell.CustomTitle.textColor setTextAlignment:self.customcell.CustomTitle.textAlignment];
self.customcell.CustomTitle.height.constant = frame.size.height;
frame = [NSString setAttributeWithString:self.customcell.CustomDes.text withLineSpacing:0.3 withSize:CGSizeMake(150, 999999999) withFont:self.customcell.CustomDes.font withLabel:self.customcell.CustomDes setLabelTextColor:self.customcell.CustomDes.textColor setTextAlignment:self.customcell.CustomDes.textAlignment];
self.customcell.CustomDes.height.constant = frame.size.height;
}
else{
CGRect frame = [NSString setAttributeWithString:self.customcell.CustomTitle.text withLineSpacing:1 withSize:CGSizeMake(337, 999999999) withFont:self.customcell.CustomTitle.font withLabel:self.customcell.CustomTitle setLabelTextColor:self.customcell.CustomTitle.textColor setTextAlignment:self.customcell.CustomTitle.textAlignment];
self.customcell.CustomTitle.height.constant = frame.size.height;
frame = [NSString setAttributeWithString:self.customcell.CustomDes.text withLineSpacing:1 withSize:CGSizeMake(227, 999999999) withFont:self.customcell.CustomDes.font withLabel:self.customcell.CustomDes setLabelTextColor:self.customcell.CustomDes.textColor setTextAlignment:self.customcell.CustomDes.textAlignment];
self.customcell.CustomDes.height.constant = frame.size.height;
}
}
[self.customcell layoutIfNeeded];
// CGFloat height = self.customcell.CustomTitle.height.constant+self.customcell.CustomDes.height.constant+189;
CGFloat height = [self.customcell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
//Add padding of 1
return height;
}
Used Github opensource library to solve the issue but didn't worked.
https://github.com/punchagency/line-height-tool
Issue still remain's, text of label's cut off, content hanging is at Required and Content is at 1000 horizontal + vertical..
Please help..
Thanks allot.
You can get the height of a string in certain bounds with the NSString method boundingRectWithSize, like
NSString* text = #"Test text";
CGRect textRect = [text boundingRectWithSize:CGSizeMake(300, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName : [UIFont systemFontOfSize:12]}
context:nil];
CGFloat textHeight = ceilf(textRect.size.height);
Use your own font and font size, and you can also add other attributes to the attributes dictionary if necessary.
Use following method to calculate your UILabel height:
add also use this method in your class.
- (CGFloat)getLabelHeight:(UILabel*)label{
CGSize constraint = CGSizeMake(label.frame.size.width, CGFLOAT_MAX);
CGSize size;
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
CGSize boundingBox = [label.text boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:label.font}
context:context].size;
size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
return size.height;}
Update your heightForRowAtIndexPath Method as per your requirement:
calculate all UI Height and return.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
Customviewcell *cell=[tableView dequeueReusableCellWithIdentifier:#"cell"];
CGFloat totalHeight = 0;
cell.CustomTitle.text=justTitles[indexPath.row];
//get label Height
totalHeight += [Helper getLabelHeight:cell.CustomTitle];
cell.Customimage.image = [UIImage imageNamed:justThumbs[indexPath.row]];
CGFloat imageHeight = cell.Customimage.frame.size.height; //or Add image height here
totalHeight += imageHeight;
return totalHeight;}
In iOS8+ you can use self sizing cells:
http://www.appcoda.com/self-sizing-cells/
Auto Resizing of Cell is available in Ios 8.0, The issue my deployment target was ios 7.0, which is causing the layout issues.
Please refer to these articles:
http://www.appcoda.com/self-sizing-cells/
The Code is in swift but need to do same thing's in objective c as well.
This will also help's you.
http://useyourloaf.com/blog/self-sizing-table-view-cells.html

UITableView frame base on number of cell

As My title said that i want to set frame of UITableView base on number of cell. Cell's height of UITableView is dynamic it is not fix, for do this i apply my following logic
NOTE: I added UITableView on UIScrollView so it is easy to scroll/see whole table's content. And i know UITableView has own scrollView but in my project i need set height of UITableView base on number of cell (dynamic height of cell).
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self adjustHeightOfTableview]; // custom method for set height of tableView
}
- (void)adjustHeightOfTableview
{
CGRect frame = self.tblView.frame;
frame.size.height = tblHeight; // tblHeight is CGFloat, declare in .h file
self.tblView.frame = frame;
self.scrollView.contentSize = CGSizeMake(300, self.tblView.frame.origin.y + self.tblView.frame.size.height);
}
And get from
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%d", indexPath.row);
NSString *AnswerHeight = [[self.listOfQuestions objectAtIndex:indexPath.row] objectForKey:#"Answer"]; // get
CGSize constraint = CGSizeMake(queWidth - (10.0f * 2), 20000.0f);
CGSize size = [AnswerHeight sizeWithFont:[UIFont fontWithName:#"OpenSans-Bold" size:12] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = MAX(size.height, 52);
tblHeight = tblHeight + height + (10.0f * 2) - 1.5; // set tblHeight
return height + (10.0f * 2) - 1.5;
}
But problem is that heightForRowAtIndexPath call 2 times so i am not able to get proper tblHeight so my tableView's height not set properly.
Why my heightForRowAtIndexPath call 2 times or How can i solve my problem ?? or any other solution for set tableView's frame base on dynamic number of cell ??
Just for understanding
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 5;
}
why to not make empty footerView.
UIView *footerView=[[UIView alloc]initWithFrame:CGRectZero];
self.taskListTable.tableFooterView=footerView;
and then change the table frame where you thinks it will change:
like in viewWillApear or any of your action
self.taskListTable.frame= CGRectMake(0,0, 320, self.view.bounds.size.height);
Each row will call heightForRowAtIndexPath everytime it will appear in the visible rows, so the solution is quite simple.
Create an NSMutableArray, with number of rows as your table view, then everytime you calculate the height of a row, just change that array's index for that indexPath's row with the height of that row.
And whenever you want the height of the table, just sum all your array's rows, that should do it.
If you have any more clarifications, just ask (Y)
heightForRowAtIndexPath will call for each row which is going to visible.
You can calculate table height in viewDidAppear or ViewWillApearMethod.
for (int i =0 ; i < self.listOfQuestions.count; i++) {
NSString *AnswerHeight = [[self.listOfQuestions objectAtIndex:i] objectForKey:#"Answer"]; // get
CGSize constraint = CGSizeMake(queWidth - (10.0f * 2), 20000.0f);
CGSize size = [AnswerHeight sizeWithFont:[UIFont fontWithName:#"OpenSans-Bold" size:12] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = MAX(size.height, 52);
tblHeight = tblHeight + height + (10.0f * 2) - 1.5; // set tblHeight
}
Check my updated answer

Table cell label not wrapping last line in iOS7

I am getting very strange problem in my app. I am creating table view cell dynamically. And adjusting height of cell label according to content.
My problem is that my cell label is not showing last line after wrapping means if there are total three lines for my label then label shows only two lines. The code was working on iOS6 but on iOS 7 it is giving problem.
[labelCell.contentView bringSubviewToFront:labelCell.textLabel];
labelCell.textLabel.numberOfLines = 0;
labelCell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
NSString *fieldLabel = labelCell.textLabel.text;
CGSize textSize = [self sizeForLabelWithString:fieldLabel width:600.0f andIndexPath:indexPath];
float newHeight = textSize.height+22.0f;
labelCell.height = newHeight;
use this method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *fieldLabel = labelCell.textLabel.text;
CGSize textSize = [fieldLabel sizeWithFont:[UIFont fontWithName:#"Georgia" size:17.0f] constrainedToSize:CGSizeMake(600, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping];
float newHeight = textSize.height+22.0f;
return newHeight;
}
Add below code to cellForRowAtIndexPath
UILabel *lblfield=[[UILabel alloc] init];
NSString *fieldLabel=[NSString stringWithFormat:#"%#",labelCell.textLabel.text];
CGSize textSize = [fieldLabel sizeWithFont:[UIFont fontWithName:#"Georgia" size:17.0f] constrainedToSize:CGSizeMake(600, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping];
float newHeight = textSize.height+22.0f;
lblfield.frame=CGRectMake(10, 0, 600, newHeight);
lblfield.backgroundColor=[UIColor clearColor];
lblfield.text=strusername;
[cell addSubview:lblfield];
I was having the same issue and just stumbled across this. My label was resizing to the correct size, but it wasn't showing the last line on any labels that wrapped to several lines. Moreover, when I selected/unselected the cell, I could suddenly see the last line.
I was able to resolve the issue by adding a small fudge factor (2 points) to the height of my cell. I updated the value to use for the height of the cell from:
return labelHeight + 2 * kTopAndBottomPadding;
to:
return labelHeight + 2 * kTopAndBottomPadding + 2.f;
You are changing the label height based on the content. So you need to handle the cell height dynamically or need to set the height of a cell based on the content(Max among the string.).
i.e
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return MAXStringHeght;
}
and try once.

Resources