Dynamic label width in UITableView [duplicate] - ios

This question already has answers here:
Change the label width dynamically inside a UITableViewCell
(3 answers)
Closed 9 years ago.
I am trying to set the label width dynamically based on the content of the other label on the same line.
I am implementing the logic inside the "cellForRowAtIndexPath"
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"rowNumber:%d", indexPath.row);
EntityTableViewCell *cell = nil;
static NSString *CellIdentifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.companyNameLabel.text = [group1 objectAtIndex:indexPath.row];
cell.companyCROfficeAddrLabel.text = [group2 objectAtIndex:indexPath.row];
cell.companyBusinessAddrLabel.text = [group3 objectAtIndex:indexPath.row];
cell.timestamp.text = [Utils getDateTimeString:[group4 objectAtIndex:indexPath.row]];
[cell.companyLogoImageView setImageWithURL:[NSURL URLWithString:[myArray objectAtIndex:indexPath.row] ]
placeholderImage:[UIImage imageNamed: #"profile-image-placeholder"]
options:indexPath.row == 0 ? SDWebImageRefreshCached : 0];
//logics to dynamically change the label width to maximum utilize the realstate
CGFloat timeStampWidth = [cell.timestamp.text sizeWithFont:cell.timestamp.font].width;
CGFloat ksCompanyNameLableMaxWidth = 235;
NSLog(#"timeStampWidth:%f", timeStampWidth);
CGSize companyNameLableSize = CGSizeMake((ksCompanyNameLableMaxWidth - timeStampWidth), cell.companyNameLabel.frame.size.height);
CGRect newFrame = cell.companyNameLabel.frame;
newFrame.size = companyNameLableSize;
cell.companyNameLabel.frame = newFrame;
NSLog(#"companyLableWidth:%f", newFrame.size.width);
return cell;
}
There are multiple problems with this code.
As the table is initialised, although this piece of code is called for every cell. The width of the label is still set as the size in the story board. On the other side, if I scroll down, the label is displayed correctly as designed in the code.
Because I also have a tabView controller within my app, whenever I switch between the tabs, the dynamically populated label width is getting updated with the default label width set in the storyboard again.
Could someone please tell me what I did wrong and suggest some solution?
Thanks

try this code
#define FONT_SIZE 14.0f
#define CELL_CONTENT_MARGIN 8.0f
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
NSString *text = [BugComments_text objectAtIndex:indexPath.row];
CGSize constraint = CGSizeMake(tableView.frame.size.width - (CELL_CONTENT_MARGIN * 2), 20000.0f);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping];
CGFloat height = MAX(size.height+40, 60.0f);
return height + (CELL_CONTENT_MARGIN * 2);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *text = [arrayofdata objectAtIndex:indexPath.row];
CGSize constraint = CGSizeMake(tableView.frame.size.width - (CELL_CONTENT_MARGIN * 2), 20000.0f);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping];
[cell.Content_Text setText:text];
cell.Content_Text setFrame:CGRectMake(CELL_CONTENT_MARGIN, CELL_CONTENT_MARGIN+cell.Content_Email.frame.origin.y+cell.Content_Email.frame.size.height, tableView.frame.size.width - (CELL_CONTENT_MARGIN * 2), MAX(size.height,20.0f))];
}

Try this in your cellForRowAtIndexPath for dynamically setting the company label text,
CGSize textSize = {
200.0, // limit width
20000.0 // and height of text area
};
CGSize contentSize = [yourText sizeWithFont:[UIFont systemFontOfSize:17.0] constrainedToSize:textSize lineBreakMode:NSLineBreakByWordWrapping];
CGFloat contentHeight = contentSize.height < 36? 36: contentSize.height; // lower bound for height
CGRect companyLabelFrame = [cell.companyNameLabel frame];
companyLabelFrame.size.height = contentHeight;
[cell.companyNameLabel setFrame:companyLabelFrame];
cell.companyNameLabel setText:yourText];

I finally solve the problem by clearly specifying it again and get the answer from #rdelmar
please look the link Here

Related

Fit Text in UItableView Cell

I have a long label that is my first label, and I want to fit it in my cell. This is what I have but it isn't working.
I have a custom UITabelviewCell with a few labels in it.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
case 0:{
CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);
CGSize size = [diningHallTimes[indexPath.row][#"description"] sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping];
CGFloat height = MAX(size.height, 44.0f);
return height + (CELL_CONTENT_MARGIN * 2) + 40;
// return myStringSize.height;
break;
}
default:
return 40;
break;
}
}
Here is cell for row
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
DiningInfoTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);
CGSize size = [diningHallTimes[indexPath.row][#"description"] sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping];
CGFloat height = MAX(size.height, 44.0f);
CGFloat the = height + (CELL_CONTENT_MARGIN * 2);
cell.descriptionLabel.numberOfLines = 0;
[cell.descriptionLabel setFrame:CGRectMake(20, 0, 280, the)];
NSLog(#"%f", the);
cell.descriptionLabel.text = diningHallTimes[indexPath.row][#"description"];
cell.daysLabel.text = diningHallTimes[indexPath.row][#"days"];
cell.timeLabel.text = diningHallTimes[indexPath.row][#"time"];
return cell;
}
But then this is what my cell looks like
Not sure why this is happening, I am running iOS8, but I need it to work for both is and 7.
Thanks for the help in advance.
You need to calculate boundingRectWithSize: for label text. Also need to calculate number of lines required for updated content also. Try this below method to calculate new frame for label.
- (UILabel *) updateLabelFrame:(UILabel *)label {
CGRect lblRect = label.frame;
CGSize maxSize = CGSizeMake(label.frame.size.width, MAXFLOAT);
CGRect labelRect = [label.text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:label.font} context:nil];
CGFloat labelHeight = labelRect.size.height;
// For below line 16 is default height (The height before content is set). Change this value
// as per your requirement
int lines = labelHeight / 16; // Here 16 is a fix height (default height) for label.
[label setNumberOfLines:lines];
lblRect.size.height = labelHeight;
[label setFrame:lblRect];
return label;
}
Edit:
You can place this method any where you want. Like some base class or in same view controller. Here I modified above method which will return label with dynamic frame.
For your case you need to place this method in view controller class. Then call this method in -cellForRowAtIndexPath after content is updated for label. See below code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
DiningInfoTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);
CGSize size = [diningHallTimes[indexPath.row][#"description"] sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping];
CGFloat height = MAX(size.height, 44.0f);
CGFloat the = height + (CELL_CONTENT_MARGIN * 2);
cell.descriptionLabel.numberOfLines = 0;
[cell.descriptionLabel setFrame:CGRectMake(20, 0, 280, the)];
NSLog(#"%f", the);
cell.descriptionLabel.text = diningHallTimes[indexPath.row][#"description"];
cell.daysLabel.text = diningHallTimes[indexPath.row][#"days"];
cell.timeLabel.text = diningHallTimes[indexPath.row][#"time"];
// Update descriptionLabel height.
cell.descriptionLabel = [self updateLabelFrame:cell.descriptionLabel];
return cell;
}
You have to set up Autolayout constraints for the label and create a prototype cell with the content. And then you have to return then you have to get the height of the cell.
For reference , you could see the following video
https://www.youtube.com/watch?v=6KImie4ZMwk
Try [yourLabel sizeToFit] or you may use [yourLabel sizeThatFit:CGFrame];

How to set cell height based on UILabel inside custom cell

I have used UITableView to display items from database and I have created custom UITableViewCell which has a few labels. My largest label is name Description.
Each cell can have different height but I can't figure out how to set dynamic cell height.
All I get is three dots at the end of the label and height for cell is always about 50.
How to make cell height based on label inside it?
#define FONT_SIZE 14.0f
#define CELL_CONTENT_WIDTH 320.0f
#define CELL_CONTENT_MARGIN 10.0f
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *text = _orderProperty.Description;
CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE]
constrainedToSize:constraint
lineBreakMode:NSLineBreakByWordWrapping];
CGFloat height = MAX(size.height, 44.0f);
return height + (CELL_CONTENT_MARGIN * 2);
}
UPDATE
static NSString *CellIdentifier = #"orderDetailCell";
OrderDetailCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
[tableView registerNib:[UINib nibWithNibName:#"OrderDetailCustomCell" bundle:nil] forCellReuseIdentifier:CellIdentifier];
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}
OrderProperty* item = [_list objectAtIndex:indexPath.row];
cell.Title.text = item.Title;
NSString* _description = item.Description;
cell.Description.text = _description;
You can try something like this
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
int rowHeight =0.0f;
NSString *stringToSize=[NSString stringWithFormat:#"%#",longStringValue];
CGSize size = [stringToSize
sizeWithFont:[UIFont systemFontOfSize:15.0f]
constrainedToSize:CGSizeMake(300, 6000)
lineBreakMode:NSLineBreakByWordWrapping];
rowHeight = size.height+10;
return rowHeight;
}
you may get the longStringValue from the array that you are using as a dataSource for your table
You can use following category on NSString, which use NSTextContainer and NSTextContainer to calculate the exact size of your text.
NSString+Calculation.m
And your final code will be like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *text = _orderProperty.Description;
CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);
CGSize size = [text usedSizeForMaxWidth:constraint.width
withFont:[UIFont systemFontOfSize:FONT_SIZE]];
CGFloat height = MAX(size.height, 44.0f);
return height + (CELL_CONTENT_MARGIN * 2);
}
sizeWithFont is now deprecated, please use the method below to set the height of the sell according the the text within the label.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
OrderProperty* item = [_list objectAtIndex:indexPath.row];
NSString *text = item.Description;
NSDictionary *stringAttributes = [NSDictionary dictionaryWithObject:[UIFont fontWithName:#"Helvetica" size:13] forKey:NSFontAttributeName];
CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);
CGSize expectedLabelSize = [content boundingRectWithSize: constraint options:NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin attributes:stringAttributes context:nil].size;
return expectedLabelSize.height+CELL_CONTENT_MARGIN * 2);
}
Obviously you will need to adjust the string attributes to whatever you are using. These are simply for calculating the required height of the cell.

Cannot change frame of a UILabel in a UITableViewCell when first appearing (using Autolayout)

I have a prototype cell inside my UITableView which contains a UILabel. I want to dynamically change the size of the label (and the cell) depending on the size of the text in the label.
I create the prototype cell in cellForRowAtIndexPath like this:
static NSString *CellIdentifier = #"ProgramDetailCell";
ProgramDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.descriptionLabel.text = self.program.subtitle;
return cell;
Then my ProgramDetailCell has the following implementation:
#implementation ProgramDetailCell
- (void)layoutSubviews {
[super layoutSubviews];
[self.descriptionLabel sizeToFit];
}
#end
The first time the cell is displayed, layoutSubviews is called, but the descriptionLabel does not get resized. However, if I scroll down the table and back up again, the "reused" cell appears with the label correctly resized!
Why does it not work the first time the cell is displayed - and what do I need to do to fix it.
Thanks in advance!
In xib, go to first tab, under Interface Builder Document , uncheck use Auto Layout box.
It doesn't work because you are using auto layout. You need some auto layout way to achieve this.
Here's the solution.
Step 1 :
Have the UILabel Pinned to Top and Bottom of the UITableViewCell. You can achieve this through the Interface Builder by having Vertical Constraint on UILabel from top and bottom of the cell. This will make the UILabel increase in height if the cell's height increase or vice versa.
Step 2:
In your UIViewController in - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath method calculate the size of the UITableViewCell.
You can calculate it using :
CGRect textFrame = [YourText boundingRectWithSize:CGSizeMake(width of the label, FLT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:Your Font} context:nil];
now return the textFrame.size.height + padding if present.
Step 3 :
You will achieve what you wanted --- "dynamically change the size of the label (and the cell) depending on the size of the text in the label" after compiling and running even for the first time.
After retrieving all data from server you can get height of that UILabel using this method.
CGSize maximumSize = CGSizeMake(210, 9999);
for (int i = 0; i < [_arrProductList count]; i++) {
float row_height = 0.0f;
ProductInformation *product_obj = [_arrProductList objectAtIndex:i];
CGSize desc_size = [self measureHeightForText:product_obj.product_desc forFont: [UIFont systemFontOfSize:14] forSize:maximumSize];
row_height = row_height + desc_size.height;
// [_arrRowHeights addObject:[NSString stringWithFormat:#"%f", row_height]]; You can take it into array.
}
[tableView reloadData];
And here i have given description of measureHeightForText:. This logic is working in all iOS5,iOS6,iOS7.
-(CGSize)measureHeightForText:(NSString *)strText forFont:(UIFont *)font forSize:(CGSize)size{
if (!testingLabel) {
testingLabel = [[UILabel alloc] init];
// testingLabel.font = [UIFont fontWithName:[AppHandlers zHandler].fontName size:16];
testingLabel.text = #"";
testingLabel.numberOfLines = 0;
}
testingLabel.text =strText;
testingLabel.font = font;
CGSize expectedSize = [testingLabel sizeThatFits:size];
return expectedSize;
}
And then update size of your label according it. This is working fine for me. I am using it.
Because when layoutSubviews is called your descriptionLabel's text is not set yet. And when you scroll, the text is set. So it is correct now.
I suggest you call sizeToFit after you set the text.
static NSString *CellIdentifier = #"ProgramDetailCell";
ProgramDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.descriptionLabel.text = self.program.subtitle;
[cell.descriptionLabel sizeToFit];
return cell;
Call this in heightForRowAtIndexPath and manually calculate the height
static NSString *CellIdentifier = #"ProgramDetailCell";
ProgramDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.descriptionLabel.text = self.program.subtitle;
[cell.descriptionLabel sizeToFit];
return cell.descriptionLabel.frame.size.height+cell.descriptionLabel.frame.origin.y;
You can use below code:
//Calculate the expected size based on the font and linebreak mode of your label
// FLT_MAX here simply means no constraint in height
CGSize maximumLabelSize = CGSizeMake(296, FLT_MAX);
CGSize expectedLabelSize = [yourString sizeWithFont:yourLabel.font constrainedToSize:maximumLabelSize lineBreakMode:yourLabel.lineBreakMode];
//adjust the label the the new height.
CGRect newFrame = yourLabel.frame;
newFrame.size.height = expectedLabelSize.height;
yourLabel.frame = newFrame;
If your requirement is dynamically changing label height mean follow the below link. When we need to change height of label we need to change the row height also:
Resizing UILabel not quite working
Use below code for dynamic height set in cell
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *messagetext=[NSString stringWithFormat:#"%#",[[arryStoryView objectAtIndex:indexPath.row] valueForKey:#"messagetext"]];
CGSize StoryTextSize= [messagetext sizeWithFont:[UIFont fontWithName:#"Georgia" size:17.0f] constrainedToSize:CGSizeMake(300, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping];
int TotalHeight;
TotalHeight= StoryTextSize.height + 10;
return TotalHeight;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell1";
UITableViewCell *cell=[tblSendMsg dequeueReusableCellWithIdentifier:CellIdentifier];
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
NSString *storytext=[NSString stringWithFormat:#"%#",[[arryStoryView objectAtIndex:indexPath.row] valueForKey:#"storytext"]];
CGSize StoryTextSize = [storytext sizeWithFont:[UIFont fontWithName:#"Georgia" size:17.0f] constrainedToSize:CGSizeMake(300, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping];
lblStoryText.frame=CGRectMake(5, 25, [[UIScreen mainScreen] bounds].size.width-10, StoryTextSize.height+30);
int nooflines=StoryTextSize.height/16;
int i= StoryTextSize.height;
if(i%16 !=0)
{
nooflines=nooflines+1;
}
lblStoryText.numberOfLines=nooflines;
lblStoryText.font=[UIFont fontWithName:#"Georgia" size:17.0f];
lblStoryText.text=[NSString stringWithFormat:#"%#",storytext];
return cell;
}
If you are using autolayout, modifying the frame will have no effect.
Instead, you should modify the constraints.

ios - can't get the UITableView cell height to be proportional to the amount of text

I am trying to get the UITableView cells to fit the text. So far I have something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UILabel *label = nil;
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault)
reuseIdentifier:#"business"];
NSString *comment = [[items_array objectAtIndex:[indexPath row]] objectForKey:(#"comment")];
cell.textLabel.numberOfLines = 0;
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
CGSize constraint = CGSizeMake(320 - (10 * 2), 20000.0f);
CGSize size = [comment sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
label = [[UILabel alloc] initWithFrame:CGRectZero];
[label setText:comment];
[label setFrame:CGRectMake(10, 10, 320 - (10 * 2), MAX(size.height, 44.0f))];
cell.textLabel.text = comment;
}
I also have this function
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
NSString *comment = [[items_array objectAtIndex:[indexPath row]] objectForKey:(#"comment")];
CGSize constraint = CGSizeMake(320 - (10 * 2), 20000.0f);
CGSize size = [comment sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = MAX(size.height, 44.0f);
return height + (10 * 2);
}
But it isn't allocating the right amount of height to the cells of the UITableViews. I think I am making some mistakes with how/where I assign the label and the text of the cells.
Please help me understand how it should be.
Thanks!
does this get you closer?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"business"];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault)reuseIdentifier:#"business"];
}
NSString *comment = [[items_array objectAtIndex:[indexPath row]] objectForKey:(#"comment")];
CGSize constraint = CGSizeMake(320 - (10 * 2), 20000.0f);
CGSize size = [comment sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, MAX(size.height, 44.0f) + 20.0f)];
label.numberOfLines = 0;
label.lineBreakMode = UILineBreakModeWordWrap;
label.text = comment;
[cell.contentView addSubview:label];
}
You need to set height of a cell using -tableView:heightForRowAtIndexPath.
With the new code posted
Here is how I did it (I used a prototype cell so things may be different for you).
I set the the autosizing of the cell to auto size the height of the label (this may already be set for the textLabel).
Then in -tableView:heightForRowAtIndexPath I did the following:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
CGSize size = CGSizeMake(WIDTH_OF_PROTOTYPE_LABEL, CGFLOAT_MAX);
size = [TEXT_AT_INDEX_PATH sizeWithFont:FONT_OF_PROTOTYPE_LABEL constrainedToSize:size lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = size.height + (HEIGHT_OF_PROTOTYPE_CELL - HEIGHT_OF_PROTOTYPE_LABEL);
return MAX(height, HEIGHT_OF_PROTOTYPE_CELL);
}
Update 2
In looking at your code again, I see two possible problems. 1) You have the width of the textLabel wrong. 2) You should compute the do MAX as the last thing after you compute the final new height.

ios - trying to understand how cellForRowAtIndexPath and heightForRowAtIndexPath work when dealing with UITableViewCells

With the help of the good people of StackOverflow, I have these two methods:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault)
reuseIdentifier:#"business"];
NSString *comment = [[items_array objectAtIndex:[indexPath row]] objectForKey:(#"comment")];
NSLog(businessPrivacy);
// FIND IF THE BUSINESS PLAN IS PRIVATE OR NOT.
CGSize constraint = CGSizeMake(320 - (10 * 2), 20000.0f);
CGSize size = [comment sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, MAX(size.height, 44.0f) + 20.0f)];
label.numberOfLines = 0;
label.lineBreakMode = UILineBreakModeWordWrap;
label.text = comment;
[cell.contentView addSubview:label];
return cell;
}
//Cell size for word wrapping.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
NSString *comment = [[items_array objectAtIndex:[indexPath row]] objectForKey:(#"comment")];
CGSize constraint = CGSizeMake(320 - (10 * 2), 20000.0f);
CGSize size = [comment sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = MAX(size.height, 44.0f);
return height + (10 * 2);
}
The effect that they have is reducing font size from default font size, and dynamically resizing to ALMOST fit the entire text of a comment no matter how long that text might be.
What I am confused with is that I have some of the same code in both methods, and I dont know how it really should look like. I do know both methods are getting called, but not sure what should be where so they would work properly.
Thanks in advance for advice.
It looks like you need them in both. I have rewritten your code with comments so that you can make sense of it:
//This function is used to create the cell and the content of the cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Here you are allocating a new cell with a reuse identifier. This is only needed if
//you plan on reusing the cells and using [tableView dequeueReusableCellWithIdentifier:cellIdentifier]
//Reusable cells will save you some memory and make your tableview work more smoothly,
//however here you are not selecting from the reusable pool, and are instead making a new cell each time
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault)
reuseIdentifier:#"business"];
//This is pulling the text for the comment, it is obviously necessary
NSString *comment = [[items_array objectAtIndex:[indexPath row]] objectForKey:(#"comment")];
NSLog(businessPrivacy);
// FIND IF THE BUSINESS PLAN IS PRIVATE OR NOT.
// This is creating a constraint size for the label you are making
CGSize constraint = CGSizeMake(320 - (10 * 2), 20000.0f);
// This is determining the size that you will need for the label based on the comment
// length and the contraint size
CGSize size = [comment sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
// Here you are creating your label and initializing it with the frame that you have
// determined by your size calculations above
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, MAX(size.height, 44.0f) + 20.0f)];
// setting up the label properties
label.numberOfLines = 0;
label.lineBreakMode = UILineBreakModeWordWrap;
label.text = comment;
// adding the label view to the cell that you have created
[cell.contentView addSubview:label];
// return the cell for the table view
return cell;
}
//Cell size for word wrapping.
//This method will determine how tall each row needs to be.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
//Here you are getting the comment again. This is necessary to determine how tall you need
//the cell to be
NSString *comment = [[items_array objectAtIndex:[indexPath row]] objectForKey:(#"comment")];
// Again you are getting the constraints because you are going to us this size
// to constrain the height of the cell just like you determined the size for the label.
CGSize constraint = CGSizeMake(320 - (10 * 2), 20000.0f);
// Again, determining the size that we will need for the label, because it will drive
// the height of the cell
CGSize size = [comment sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
// Finally, we can determine the height for the cell!
CGFloat height = MAX(size.height, 44.0f);
// return the height, which is the height of the label plus some extra space to make
// it look good.
return height + (10 * 2);
}
So basically, the cellForRow... function creates the content of the cell and the heightForRow... function determines the height. You need all of that information in both of them because the cell function only creates the content of the cell while the height function creates the height of the row and the comment, constraint, and size are all important in determine the label size for the cell content and the height of the cell.
So heightForRowAtIndexPath, as its name suggests, only deal with the height of the cells. i.e. the return value (a CGFloat) of that function is the height of the cell at the specified indexpath.
cellForRowAtIndexPath on the other hand sets up the actual cell, including all its subviews. What happens is, say some cells have a 5-line UILabel and some have 1-line. Then You need to adjust the size of the cell in heightForRowAtIndexPath, but at the same time you also need to adjust the size of the UILabel within the cell in cellForRowAtIndexPath. The latter will not automatically happen. You need to take care of both yourself.
If you have to write the same code two or more times, you should consider extracting the duplicate code into a method or function. But you have some other problems we should address.
Let's declare some constants so we don't have to repeat numbers all over the code:
static const CGFloat kLabelMargin = 10.0f;
static const CGFloat kLabelWidth = 320.0f - 2.0f * kLabelMargin;
One line you have duplicated in both methods looks up the comment for a row. Let's make a method to do that:
- (NSString *)commentForRowAtIndexPath:(NSIndexPath *)indexPath {
return [[items_array objectAtIndex:[indexPath row]] objectForKey:#"comment"];
}
The other duplicated code computes the height of the row, so let's make a function to return that:
static CGFloat heightForComment(NSString *comment) {
CGSize constraint = CGSizeMake(kLabelWidth, 20000.0f);
CGSize size = [comment sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
return MAX(size.height, 44.0f) + 2.0f * kLabelMargin;
}
Now we can rewrite your tableView:heightForRowAtIndexPath: method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return heightForComment([self commentForRowAtIndexPath:indexPath]);
}
Finally, let's rewrite your tableView:cellForRowAtIndexPath: method. We will make it properly reuse cells (to avoid wasting memory), and we'll make it use the new method and function we created.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *const kReuseIdentifier = #"business";
static const NSInteger kLabelTag = 1;
NSLog(#"%#", businessPrivacy);
// FIND IF THE BUSINESS PLAN IS PRIVATE OR NOT.
NSString *comment = [self commentForRowAtIndexPath:indexPath];
CGRect labelFrame = CGRectMake(0, 0, kLabelWidth, heightForComment(comment));
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kReuseIdentifier];
UILabel *label;
if (cell) {
label = (UILabel *)[cell.contentView viewWithTag:kLabelTag];
label.frame = labelFrame;
} else {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kReuseIdentifier];
label = [[UILabel alloc] initWithFrame:labelFrame];
label.tag = kLabelTag;
label.numberOfLines = 0;
label.lineBreakMode = UILineBreakModeWordWrap;
[cell.contentView addSubview:label];
}
label.text = comment;
return cell;
}

Resources